# MATTR VII Platform API Overview
URL: /docs/api-reference
Description: The MATTR VII Platform API defines a set of capabilities that can be used to manage and interact with a MATTR VII tenant.
The MATTR VII API defines a set of capabilities that can be used to manage and interact with a MATTR VII tenant. This includes managing a Verifiable Credential across its lifecycle (issue-hold-verify) as well as various tenant administration and management tasks such as setting up a custom domain, creating identifiers and configuring issuance and verification workflows.
## OpenAPI Specifications [#openapi-specifications]
Download the complete OpenAPI specifications for the MATTR VII Platform API:
## Getting Started [#getting-started]
### Choose an endpoint [#choose-an-endpoint]
Select an endpoint to make a request to. The following resources might be helpful:
* The API Reference offers an exhaustive list of all available endpoints and their
request structures in different languages.
* Different tutorials and guides can be used to learn what endpoints are required for specific
capabilities and workflows.
* Refer to the [Access control](/docs/platform-management/access-control) section to learn more
about what endpoints your client will be able to access.
We recommend using the MATTR VII [Postman
collection](#postman-collection)
to make requests to your MATTR VII tenant. While this isn't an explicit
requirement it can really speed things up.
### Obtain an access token [#obtain-an-access-token]
Most of the MATTR VII endpoints are protected and require providing a bearer access token when
making a request. If you are making a request to an unprotected endpoint (as detailed in the
API Reference), you do not need to obtain an access token and can continue to the
next step.
Use your [access credentials](/docs/platform-management/portal#getting-started) and make a request of the
following structure to obtain an access token:
```http title="Request"
POST https://{auth_server}/oauth/token
```
* `auth_server` : Replace with the `auth_url` value obtained when you created your tenant.
```json title="Request Body"
{
"client_id": "F5qae****************************",
"client_secret": "Wzc8J**********************************************************",
"audience": "learn.vii.au01.mattr.global",
"grant_type": "client_credentials"
}
```
* `client_id` : Replace with the `client_id` value obtained when you created your tenant.
* `client_secret` : Replace with the `client_secret` value obtained when you created your tenant.
* `audience` : Replace with the `audience` value obtained when you created your tenant.
* `grant_type` : Always use `client_credentials` as a static value, regardless of your specific
[login credentials](/docs/platform-management/portal#getting-started).
*Response*
```json title="Response body"
{
"access_token": "eyJhb********************************************************************", // [!code focus]
"expires_in": 14400,
"token_type": "Bearer"
}
```
* The returned `access_token` will enable access to endpoints as per the role assigned to the
client. Refer to [Access control](/docs/platform-management/access-control) for more Information.
* You will need to obtain a new access token whenever it expires. Our
[Postman collection](#postman-collection)
includes a pre-request script that obtains an access token when it is missing or has expired.
### Construct the request [#construct-the-request]
Construct an API request using the selected endpoint path and the `tenant_url` value obtained when you created your tenant:
```http title="Request template"
{method} https://{tenant_url}/{path}
```
For example, a request to
[retrieve all IACAs](/docs/api-reference/platform/iaca/list-mobile-credential-iaca) from a
tenant whose `tenant_url` is `learn.vii.au01.mattr.global` should be constructed as follows:
```http title="Request example"
GET https://learn.vii.au01.mattr.global/v2/credentials/mobile/iacas
```
If the operation has a request body you should structure it too, based on the details provided in
the [API Reference](/docs/api-reference) or relevant tutorial.
Whatever tool or language your are using to make the request, make sure you
include the `access_token` in the request header when making requests to
protected endpoints. Refer to the [API Reference](/docs/api-reference) for request
samples.
### Handle the response [#handle-the-response]
The endpoint would respond with a standard
[HTTP status code](https://www.rfc-editor.org/rfc/rfc9110.html#name-status-codes) and a response
body. These differ between endpoints and are detailed in the API Reference.
You can now adjust your implementation to handle these responses to achieve the desired outcome.
## Concepts [#concepts]
### Pagination [#pagination]
Most list operations in the API enable cursor pagination using the `cursor` and `limit` query parameters:
**Example on [Retrieve List of Credentials](#operation/retrieveListCreds)**
```
GET https://{tenant-url}/v2/credentials
?limit=100
&cursor=Y3JlYXRlZEF0PTIwMjAtMTAtMDhUMjMlM0ExMyUzQTE3Ljg5NtZGUxZWEyNzQ4MWI4
```
* `limit`: determines how many entries are returned in that request, with a maximum value of 1000.
* `cursor`: sets the location in the retrieved list to get the next batch of entries from. This is based on the returned `nextCursor`, found at the beginning of each returned range and identifies the last object in the list.
Requesting an entry after the last list value will return an empty `data` object:
```json
{
"data": []
}
```
Not providing a query parameter defaults the response to return the first range of entries with a `limit` of 100.
### Authorization [#authorization]
Access to the API is granted by our authorization provider. Use the `auth_url`, `audience`, `client_id` and `client_secret` provided with your tenant details to [make a request](/docs/api-reference/platform/security/authToken) to receive a bearer token from the auth provider. This token must then be used as an `authorization` header for all requests to protected endpoints (this is required for the majority of operations).
The returned bearer token will only enable access to endpoints as per your client's defined role. Refer to [Access Control](#access-control) for more information.
### Access control [#access-control]
MATTR VII uses **Role-Based Access Control (RBAC)** to manage permissions and access within a tenant. Each role grants access to specific capabilities, ensuring that users or clients only have access to the functionalities they need. Below is a list of available roles and their descriptions:
* Tenant admin: Has full access to all tenant capabilities. This role is
assigned to the default client when a new tenant is created.
* Issuer: Has access to capabilities required for issuing and managing
credentials of different formats across different channels.
* Verifier: Has access to capabilities required for verifying credentials
of different formats across different channels.
* DTS provider: Has access to capabilities required for managing a
Digital trust service (DTS).
* DTS consumer: Has access to capabilities required to consume DTS
information from a tenant.
* Auditor: Has read-only access to analytics data.
Each restricted endpoint includes a `Roles` property that indicates what roles are required to access it.
### Rate limiting [#rate-limiting]
To ensure platform stability and consistent resource allocation, API requests are subject to rate limiting. This helps protect the service from excessive usage patterns and ensures consistent performance for all users.
Every response from the MATTR VII Platform API includes the following rate limit headers:
* `x-ratelimit-limit`: Maximum requests allowed in time window.
* `x-ratelimit-remaining`: Requests remaining in current window.
* `x-ratelimit-reset`: Timestamp when the window resets.
If you exceed the rate limit, the API will return a `429 Too Many Requests` response, and you should pause further requests until the rate limit resets:
```http title="Rate limit exceeded response"
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
x-ratelimit-limit: 10
x-ratelimit-remaining: 0
x-ratelimit-reset: 1768791002
{
"message": "Too many requests, please try again later."
}
```
If you're consistently hitting rate limits or believe your use case requires adjusted limits, please [contact our support team](mailto:dev-support@mattr.global) to discuss your requirements.
## Previous versions [#previous-versions]
This is the latest version of the MATTR VII Platform API. Refer to the [MATTR VII Changelog](/docs/resources/changelog/vii#supported-versions) for links to previous versions.
## Postman collection [#postman-collection]
The MATTR VII Platform API postman collection provides a simple interface to use the MATTR VII Platform API. Its categorized endpoints, sample request body and pre-request scripts make it easier and quicker to interact with your tenant.
Follow the steps below to get started with the Postman collection:
**Step 1: Install Postman**
Visit the Postman [downloads page](https://www.postman.com/downloads/) to install a desktop client for your system.
**Step 2: Download collection and environment files**
Download the following files to your local machine:
* [`Platform API Collection`](/files/mattr_vii_platform_postman_collection.json): This Postman Collection includes API operations and some configuration.
* [`Tenant Environment`](/files/mattr_vii_platform_postman_environment.json): This file defines variables for your MATTR VII tenant. You will need to update some of these variables with your tenant details in a later step.
**Step 3: Import collection and environment files into Postman**
1. Open Postman.
2. Select the **Import** button in the *My Workspace* area.
3. Select the local versions of the *Tenant Environment* and the *Platform API Collection* files you downloaded in the previous step.
**Step 4: Update environment variables**
1. Select the **Environments** button in the *My Workspace* sidebar.
2. Hover over the *MATTR VII Tenant* name in the environments list and select **Set Active** (you should see it marked as active).
3. Select the *MATTR VII Tenant* environment.
4. Update the following variables with your tenant details:
* `baseUrl`: Replace with your tenant's URL.
* `auth0Base`: Replace with your Authentication server URL.
* `tenantClientId`: Replace with your Client ID.
* `tenantClientSecret`: Replace with your Client Secret.
* `tenantAudience`: Replace with your tenant's URL.
If you are unsure of any of these details, please [contact us](mailto:dev-support@mattr.global).
**Step 5: Try it out**
1. Select the **Collections** button in the *My Workspace* sidebar.
2. Select the MATTR VII API collection.
3. Select the *Retrieve all IACAs* endpoint (You can find it under *Tenant configuration > Identifiers > IACA*).
4. Select **Send** in the top right corner of the request pane.
5. The response should be displayed in the *Response* pane (If this is a new tenant, the response is likely to be empty).
# MATTR VII Management API Overview
URL: /docs/api-reference/management-api-reference-overview
Description: The MATTR VII Management API enables administrators to perform actions that span across multiple tenants.
The MATTR VII Management API enables administrators to perform actions that span across multiple tenants. It provides capabilities for creating, updating, and managing tenants, as well as configuring access controls, defining who can access each tenant, with what roles, and which permissions. This API is essential for orchestrating large-scale deployments and maintaining centralized oversight of your digital trust infrastructure.
## OpenAPI Specifications [#openapi-specifications]
Download the complete OpenAPI specifications for the MATTR VII Management API:
## Getting Started [#getting-started]
### Obtain an access token [#obtain-an-access-token]
Before you can make any API requests to manage your MATTR VII tenants, you must complete
authentication by obtaining a management API access token from our authentication provider.
Use your management API client credentials to make a request of the following structure:
```http title="Request"
POST https://auth.manage.au01.mattr.global/oauth/token
```
```json title="Request body"
{
"client_id": "F5qae****************************",
"client_secret": "Wzc8J**********************************************************",
"audience": "https://manage.au01.mattr.global",
"grant_type": "client_credentials"
}
```
* `client_id` : Replace with the `client_id` value from your management API client credentials.
* `client_secret` : Replace with the `client_secret` value from your management API client
credentials.
* `audience` : Always use `https://manage.au01.mattr.global` as a static value, regardless of your
specific management API client credentials.
* `grant_type` : Always use `client_credentials` as a static value, regardless of your specific
management API client credentials.
These are not the same `client_id` and `client_secret` you were provided for accessing other
MATTR VII APIs, but rather unique credentials for accessing the Management APIs. If you have not
received these credentials or have any questions, please [contact
us](mailto:dev-support@mattr.global) before proceeding.
*Response*
```json title="Response body"
{
"access_token": "eyJhb********************************************************************", // [!code focus]
"expires_in": 14400,
"token_type": "Bearer"
}
```
The returned `access_token` must be used as a bearer token for all subsequent requests to
any of the protected MATTR VII Management API endpoints.
### Construct the request [#construct-the-request]
Construct an API request using the selected endpoint path and the specific region value for your
tenant. Use the following template to structure the request:
```http title="Request template"
{method} https://manage.{region}.mattr.global/{path}
```
For example, a request to
[retrieve all tenants](/docs/api-reference/management/tenants/getTenants) in the `au01` region should be constructed as follows:
```http title="Request example"
GET https://manage.au01.mattr.global/v1/tenants
```
If the operation has a request body you should structure it too, based on the details provided in
the API Reference or relevant tutorials.
Whatever tool or language you are using to make the request, make sure you
include the `access_token` in the request header when making requests to
protected endpoints. Refer to the API Reference for request
samples.
### Handle the response [#handle-the-response]
The endpoint would respond with a standard
[HTTP status code](https://www.rfc-editor.org/rfc/rfc9110.html#name-status-codes) and a response
body. These differ between endpoints and are detailed in the API Reference.
You can now adjust your implementation to handle these responses to achieve the desired outcome.
## Concepts [#concepts]
### Pagination [#pagination]
Most list operations in the API enable cursor pagination using the `cursor` and `limit` query parameters:
**Example on [retrieve all tenants](/docs/api-reference/management/tenants/getTenants)**
```
GET https://manage.au01.mattr.global/v1/tenants
?limit=100
&cursor=Y3JlYXRlZEF0PTIwMjAtMTAtMDhUMjMlM0ExMyUzQTE3Ljg5NtZGUxZWEyNzQ4MWI4
```
* `limit`: determines how many entries are returned in that request, with a maximum value of 1000.
* `cursor`: sets the location in the retrieved list to get the next batch of entries from. This is based on the returned `nextCursor`, found at the beginning of each returned range and identifies the last object in the list.
Requesting an entry after the last list value will return an empty `data` object:
```json
{
"data": []
}
```
Not providing a query parameter defaults the response to return the first range of entries with a `limit` of 100.
### Authorization [#authorization]
The Management API is a separate set of APIs to MATTR VII. It uses machine-to-machine authentication through its own credentials, which are different from your MATTR VII client credentials.
As part of onboarding you will be provided with the required details to make a call to a dedicated management API authorization provider and receive a bearer token.
This token is then used in an `authorization` header on all calls identified as requiring `bearerAuth` (this is required for the majority of management operations).
### Rate limiting [#rate-limiting]
To ensure platform stability and consistent resource allocation, API requests are subject to rate limiting. This helps protect the service from excessive usage patterns and ensures consistent performance for all users.
Every response from the MATTR VII Management API includes the following rate limit headers:
* `x-ratelimit-limit`: Maximum requests allowed in time window.
* `x-ratelimit-remaining`: Requests remaining in current window.
* `x-ratelimit-reset`: Timestamp when the window resets.
If you exceed the rate limit, the API will return a `429 Too Many Requests` response, and you should pause further requests until the rate limit resets:
```http title="Rate limit exceeded response"
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
x-ratelimit-limit: 10
x-ratelimit-remaining: 0
x-ratelimit-reset: 1768791002
{
"message": "Too many requests, please try again later."
}
```
If you're consistently hitting rate limits or believe your use case requires adjusted limits, please [contact our support team](mailto:dev-support@mattr.global) to discuss your requirements.
## Previous versions [#previous-versions]
This is the latest version of the MATTR VII Management API. Refer to the [MATTR VII Changelog](/docs/resources/changelog/vii#supported-versions) for links to previous versions.
# How does a DTS chain of trust work and what are the requirements for DTS certificates?
URL: /docs/digital-trust-service/certificates-overview
Description: How the Digital Trust Service chain of trust works, the 2-tier and 3-tier certificate models, and the requirements for DTS root CA, intermediate CA, and signer certificates used to sign VICALs and RICALs.
## Chain of trust [#chain-of-trust]
When DTS operators publish trusted lists (such as a trusted list of mDoc issuers in the form of a
[VICAL](/docs/digital-trust-service/vical-overview), or a trusted list of verifiers in the form of a
[RICAL](/docs/digital-trust-service/rical-overview)) they must ensure that consumers can verify the
authenticity and integrity of these lists. This is accomplished using a chain of trust, which is
established through a hierarchy of certificates.
Each trusted list is signed by a **signer certificate** that chains back to a DTS root CA: a VICAL
Signer Certificate (VSC) for a VICAL, or a RICAL Signer Certificate (RSC) for a RICAL. The same DTS
certificate models and requirements described on this page apply to both.
The following diagram depicts how MATTR implements the chain of trust model when signing trusted
lists:
## Certificate models: 2-tier and 3-tier [#certificate-models-2-tier-and-3-tier]
MATTR VII supports two certificate models for signing trusted lists, depending on whether an
intermediate CA certificate is included in the chain of trust:
* **2-tier model**: The chain consists of a DTS root CA certificate that directly signs the signer
certificate (VSC or RSC). This is the default model.
* **3-tier model**: The chain includes an additional DTS intermediate CA certificate between the DTS
root CA and the signer certificate (VSC or RSC). The DTS root CA signs the intermediate CA, and the
intermediate CA signs the signer certificate.
The certificate model is determined by the `useIntermediateCa` property on the DTS root CA
certificate. When set to `true`, the DTS root CA requires and uses intermediate CA certificates as
part of its chain of trust for signing operations.
The 3-tier model is only supported for [unmanaged (external)](/docs/concepts/chain-of-trust#external-certificates)
DTS certificates. You cannot enable intermediate CA certificates for managed DTS root CA
certificates, and intermediate CA certificates themselves can only be unmanaged (external). In other
words, both the DTS root CA and the DTS intermediate CA must be unmanaged to use the 3-tier model.
### Choosing a model [#choosing-a-model]
Consider the following when deciding between the two models:
* **Operational simplicity**: The 2-tier model involves fewer certificates to issue, manage, and
renew. If you do not have a specific requirement for an intermediate CA, the 2-tier model is
simpler to operate.
* **Key protection and isolation**: The 3-tier model lets you keep the DTS root CA private key
offline and use the intermediate CA for day-to-day signing. This reduces exposure of the root key
and limits the impact if an intermediate CA is compromised, as you can revoke and replace the
intermediate without rotating the root.
* **PKI alignment**: Choose the 3-tier model if your organization's existing Public Key
Infrastructure (PKI) policies require a layered hierarchy with intermediate CAs, or if you need to
delegate signing to separate intermediate authorities.
* **Management method**: The 3-tier model requires unmanaged (external) certificates for both the
root and intermediate CA. If you want MATTR VII to manage your DTS root CA, you must use the 2-tier
model.
## Certificate requirements [#certificate-requirements]
The following list describes the requirements for DTS certificates used in MATTR VII. Some of the
requirements are common across all certificates, while others are specific to the type of
certificate (DTS root CA, signer certificate). The signer certificate requirements apply to both the
VICAL Signer Certificate (VSC) and the RICAL Signer Certificate (RSC).
* When using managed DTS certificates, MATTR VII **automatically** ensures that all certificates
meet these requirements.
* When using [unmanaged (external)](/docs/concepts/chain-of-trust#external-certificates) DTS certificates, it is the responsibility of the **DTS provider** to
ensure compliance.
### Common certificate requirements [#common-certificate-requirements]
* Certificate format & basic attributes:
* PEM format must contain a valid X.509 certificate.
* Version must be v3.
* `Issuer` field must be present and valid.
* Issuer Alternative Name must be present and contain a valid email address or URI.
* Serial Number:
* Must be present.
* Must contain 1-20 digits (**Best practice**: Use a positive, non-sequential value).
* Subject attributes:
* Subject field must be present:
* Country (C): must be present and be a valid
[ISO 3166-1 alpha-2 code](https://www.iso.org/glossary-for-iso-3166.html).
* Common Name (CN): must be present and non-empty.
* Organization (O): must be present and non-empty.
* Public Key requirements:
* Subject Public Key must be present.
* Extensions:
* Certificate must include extensions.
* Duplicate extensions are not allowed (No more than one extension with the same `extnID`).
* Mandatory extensions:
* Subject Key Identifier: Must be present and non-empty.
* Key Usage:
* Must be present.
* Must match the intended use of the certificate (e.g. DTS root CA, VSC, RSC).
* Validity period:
* `NotAfter` must be after `NotBefore`.
* `NotAfter` cannot be in the past (expired certificates are invalid).
* CA certificates (DTS root CA and DTS intermediate CA) can be future dated (`notBefore` can be
in the future).
* Signer certificates (VSC or RSC) cannot be future dated (`notBefore` cannot be in the future).
* Must be within allowed limits for certificate type:
* Signer certificate (VSC or RSC): Maximum 1187 days from issuance.
* Certificate Revocation List (CRL):
* If a CRL is provided, it must be valid and signed by the DTS root CA.
* The CRL must be accessible via a valid URI.
### DTS root CA specific requirements [#dts-root-ca-specific-requirements]
* Must include the `keyCertSign` and `cRLSign` key usages.
* Basic constraints must be present and `CA` must be set to `TRUE`.
* Issuer Alternative Name must be present and contain a valid email address or URI.
* Signature must be self-signed and verifiable.
* Public key must use one of the supported public key algorithms and curves as defined in
ISO/IEC 18013-5:2021 B.3:
* ECDSA curves: `P-256`, `P-384`, `P-521`, `brainpoolP256r1`, `brainpoolP320r1`,
`brainpoolP384r1`, `brainpoolP512r1`
* EdDSA key types: `Ed25519`, `Ed448`
### DTS intermediate CA specific requirements [#dts-intermediate-ca-specific-requirements]
These requirements apply only when using the [3-tier model](#certificate-models-2-tier-and-3-tier).
* Must be signed by a valid DTS root CA that has `useIntermediateCa` set to `true`.
* Must include the `keyCertSign` key usage.
* Basic constraints must be present and `CA` must be set to `TRUE`.
* `Issuer` field must be present and must match the exact binary value of the DTS root CA
certificate subject.
* Authority Key Identifier must be present and match the DTS root CA's Subject Key Identifier.
* The country must match the country of the DTS root CA certificate.
* Must not exceed the parent DTS root CA's validity period (i.e. `notBefore` and `notAfter` must be
within the DTS root CA's validity period).
* Signature must be verifiable against the DTS root CA.
### Signer certificate specific requirements (VSC and RSC) [#signer-certificate-specific-requirements-vsc-and-rsc]
These requirements apply to both the VICAL Signer Certificate (VSC) and the RICAL Signer Certificate
(RSC).
* Must be signed by a valid signing CA: the DTS root CA in the 2-tier model, or the DTS intermediate
CA in the [3-tier model](#certificate-models-2-tier-and-3-tier).
* Common name must differ from the parent signing CA.
* `Issuer` field must be present and must match the exact binary value of the parent signing CA
certificate subject.
* Must include the `nonRepudiation` key usage exclusively.
* Extended key usage must be present and include the correct OID:
* `1.0.18013.5.1.8`.
* Signature must be verifiable against the parent signing CA.
* Authority Key Identifier must be present and match the parent signing CA's Subject Key Identifier.
* Must not exceed the parent signing CA's validity period (i.e. `notBefore` and `notAfter` must be
within the parent signing CA's validity period).
* Public key must match the CSR provided during signer creation.
### Example DTS certificates [#example-dts-certificates]
See an example of valid certificates parsed using the MATTR Labs
[X.509 certificate decoder](https://tools.mattrlabs.com/pem):
* [DTS root CA](https://tools.mattrlabs.com/pem?cert=MIICaTCCAhCgAwIBAgIKZxRbYxAhB8suGjAKBggqhkjOPQQDAjBVMQswCQYDVQQGEwJBVTEvMC0GA1UEAwwmdHV0b3JpYWwudmlpLmF1NzAxLm1hdHRybGFicy5pbyBEVFMgQ0ExFTATBgNVBAoMDEV4YW1wbGUgaW5jLjAeFw0yNTA4MjcyMjU2NDVaFw00NTA4MjgwMDAwMDBaMFUxCzAJBgNVBAYTAkFVMS8wLQYDVQQDDCZ0dXRvcmlhbC52aWkuYXU3MDEubWF0dHJsYWJzLmlvIERUUyBDQTEVMBMGA1UECgwMRXhhbXBsZSBpbmMuMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAKelCNyLAqcqNPvCxYjZIq8f4BRGC6rVmHRBhROS3pWQN5atU9OdYfpd7ZHPKEoj568bczSlnmXih9pLtTAaBqOBxzCBxDAdBgNVHQ4EFgQUPWkAFCKyDKb%252BtEoAz1gNtHZSQ7AwDgYDVR0PAQH%252FBAQDAgEGMBIGA1UdEwEB%252FwQIMAYBAf8CAQAwfwYDVR0fBHgwdjB0oHKgcIZuaHR0cHM6Ly90dXRvcmlhbC52aWkuYXU3MDEubWF0dHJsYWJzLmlvL3YxL2Vjb3N5c3RlbXMvY2VydGlmaWNhdGVzL2NhLzViZjViMGEzLWRlMTYtNGY2Ni05ZWE5LTEzNmI4YTgyMmQ4NS9jcmwwCgYIKoZIzj0EAwIDRwAwRAIgSnaPUlBoxzuhrdz2%252Fnroi0RvQI2C97ZAX%252FF%252Bx%252FLlY2QCIBnrqXjr3VdySNSwD1lfx1e66deKtux82S%252BfTdewCg%252Fl)
* [VSC](https://tools.mattrlabs.com/pem?cert=MIIClDCCAjqgAwIBAgIKRcoBxY4OKBrQpTAKBggqhkjOPQQDAjBVMQswCQYDVQQGEwJBVTEvMC0GA1UEAwwmdHV0b3JpYWwudmlpLmF1NzAxLm1hdHRybGFicy5pbyBEVFMgQ0ExFTATBgNVBAoMDEV4YW1wbGUgaW5jLjAeFw0yNTA4MjcyMjU2NDVaFw0yODExMjYyMjU2NDVaMFsxCzAJBgNVBAYTAkFVMTUwMwYDVQQDDCx0dXRvcmlhbC52aWkuYXU3MDEubWF0dHJsYWJzLmlvIFZJQ0FMIFNpZ25lcjEVMBMGA1UECgwMRXhhbXBsZSBpbmMuMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfyfYFgoFXvm4ra2cpsYC9E%252FztcgKNwxBaxbY%252BH3dCliIbWaP3Shf%252BWgGjObSw24C8QT34d8ZRvrM6jCBBgKdXKOB6zCB6DAdBgNVHQ4EFgQUF6LkXvQrrAqYtkDBjkbk9po7KHQwDgYDVR0PAQH%252FBAQDAgZAMH8GA1UdHwR4MHYwdKByoHCGbmh0dHBzOi8vdHV0b3JpYWwudmlpLmF1NzAxLm1hdHRybGFicy5pby92MS9lY29zeXN0ZW1zL2NlcnRpZmljYXRlcy9jYS81YmY1YjBhMy1kZTE2LTRmNjYtOWVhOS0xMzZiOGE4MjJkODUvY3JsMB8GA1UdIwQYMBaAFD1pABQisgym%252FrRKAM9YDbR2UkOwMBUGA1UdJQEB%252FwQLMAkGByiBjF0FAQgwCgYIKoZIzj0EAwIDSAAwRQIgPJCn7jsVyCOIolwYE7HqBll4Xvt6BHm2Iw%252BMPH78m0MCIQCC3NB8mx8G3cSNudJ7H0lQqg9QZP2r4sAX%252FPEIqxqFdA%253D%253D)
# DTS options
URL: /docs/digital-trust-service/consuming-trust-overview
Description: The options MATTR offers for publishing and consuming trust, and which category of trusted list each one serves.
MATTR offers several options for publishing and consuming trust. Each option serves one of the
categories described in [Trusted Lists](/docs/digital-trust-service/trusted-lists), so you can pick the
mechanism that matches the kind of trust you need to publish or consume.
There are different models for a Digital Trust Service to make its trust framework available for
consumption. You can download trust information from publicly available websites, from public APIs, or
from authenticated APIs (which may require onboarding and potentially involve commercial terms).
## Ecosystem policies via MATTR VII APIs [#ecosystem-policies-via-mattr-vii-apis]
**Serves:** Trusted Issuer Lists and Trusted Reader Lists.
[MATTR VII ecosystem capabilities](/docs/digital-trust-service) let a network operator publish policies
that describe which participants may issue and which may verify each credential type. Issuers, holders,
and verifiers retrieve this information through publicly available or authenticated APIs and integrate
it into their solutions.
A policy defines what credential types (identified by their **format** and **type/docType**) can be
**issued** and/or **verified** by which participants (identified by their **IACA/DID**). Retrieve the
current policy through the
[ecosystem policy API](/docs/api-reference/platform/policy/getLatestEcosystemPolicy), or consume it
through the MATTR Pi [Holder](/docs/holding/sdk-overview),
[Verifier Web](/docs/verification/remote-web-verifiers/sdks/overview), and
[Verifier Mobile](/docs/verification/remote-mobile-verifiers/sdks/overview) SDKs, or through
MATTR [GO Hold](/docs/holding/go-hold/getting-started) and
[GO Verify](/docs/verification/go-verify/getting-started).
## VICAL: standardized issuer trust lists [#vical-standardized-issuer-trust-lists]
**Serves:** Trusted Issuer Lists.
A [VICAL](/docs/digital-trust-service/vical-overview) (Verified Issuer Certificate Authority List) is a
mechanism defined in the [ISO/IEC 18013-5](https://www.iso.org/standard/69084.html) standard to support
establishing trust in networks where relying parties need to verify credentials issued by numerous
different issuers. The VICAL operator collects and validates IACAs from different issuing authorities,
then cryptographically signs them into a single list that relying parties can consume.
VICAL is the concrete, standards-based implementation of a
[Trusted Issuer List](/docs/digital-trust-service/trusted-lists#trusted-issuer-lists). See the
[VICAL overview](/docs/digital-trust-service/vical-overview),
[guide](/docs/digital-trust-service/vical-guide), and
[consumption](/docs/digital-trust-service/vical-consumption) pages to implement it.
## RICAL: reader trust lists [#rical-reader-trust-lists]
**Serves:** Trusted Reader Lists.
A [RICAL](/docs/digital-trust-service/rical-overview) (Reader Certificate Authority List) is the
mechanism for publishing which readers (verifiers) are authorized to request credentials. Holders and
wallets consume a RICAL to confirm that a verifier is trusted before presenting data to it.
RICAL is the concrete implementation of a
[Trusted Reader List](/docs/digital-trust-service/trusted-lists#trusted-reader-lists). See the
[RICAL overview](/docs/digital-trust-service/rical-overview),
[guide](/docs/digital-trust-service/rical-guide), and
[consumption](/docs/digital-trust-service/rical-consumption) pages to implement it.
## Choosing an option [#choosing-an-option]
| You want to publish or consume | Category | Use |
| -------------------------------------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------- |
| Which participants may issue or verify a credential type | Issuer and Reader lists | [Ecosystem policies via MATTR VII APIs](#ecosystem-policies-via-mattr-vii-apis) |
| A signed list of trusted issuer certificate authorities | Issuer lists | [VICAL](#vical-standardized-issuer-trust-lists) |
| A list of trusted readers (verifiers) | Reader lists | [RICAL](#rical-reader-trust-lists) |
| Certified wallets and technologies | Trusted wallets | [Wallet attestation](/docs/issuance/credential-issuance/wallet-attestation) and trust framework criteria |
Once your application consumes trust information from a DTS, verification and presentation processes are
greatly simplified. Establishing trust with every individual issuer or verifier is delegated to the DTS
operator.
# Overview
URL: /docs/digital-trust-service
## Introduction [#introduction]
Trust networks are created to enable participants (governments, organizations, businesses, end
users, etc.) to exchange data and value in different digital interactions. They can be very large or
very small, ranging from countries and government agencies to enterprises and small companies.
**Examples of trust networks**
Banks, fintech companies, and third-party providers collaborate to securely
share financial data and services with user consent. A typical use case
involves a consumer using a budgeting app to access and aggregate their
financial data from multiple bank accounts. The app requests access to the
user's bank accounts through secure APIs, and with the user’s approval,
retrieves transaction data to help them manage spending and savings, all
within a trusted network.
In the EU eIDAS, cross-border trust is established for digital identities
and signatures, enabling individuals and businesses in EU member states to
engage in secure electronic transactions. A practical use
States issue digital versions of driver’s licenses, allowing citizens to
prove their identity securely via mobile devices. A typical use case
involves a citizen using their mDL to verify their age when purchasing
alcohol at a store. The store clerk scans the digital license via a secure
reader, instantly confirming the individual’s age and identity without the
need for physical ID, making the transaction faster and more secure.
## Challenge [#challenge]
These trust networks can include numerous issuers, holders and verifiers interacting in multiple
ways and exchanging various verifiable credentials. While verifiable credentials offer some privacy
preserving and tamper evident properties to enhance trust, all network participants can benefit from
additional trust layers that make it easier to understand which participants and what interactions
can be trusted.
Furthermore, as the number of participants increases, it becomes increasingly difficult to establish
and maintain direct trust relationships with each one individually. This complexity can lead to
security vulnerabilities, poor user experiences and a breakdown in trust.
**Examples of challenges in trust networks**
Security challenges arise when an uncertified and possibly malicious
third-party provider attempts to gain access to user data. Without proper
trust layers, these providers may expose vulnerabilities that hackers can
exploit, leading to data breaches. From a user experience perspective, if
consumers cannot easily identify which third-party apps are trustworthy,
they might hesitate to use new services, creating friction and frustration.
Security challenges occur when identity credentials issued in one country
are not properly validated or if a foreign provider does not meet the same
security standards, creating an opening for identity fraud or manipulation.
From a user experience perspective, individuals attempting to use their eID
in another country may encounter long delays or service denials if the
identity verification process is not interoperable or aligned. For instance,
a German citizen trying to sign a contract in Italy might face an
unresponsive or overly complex process, leading to frustration and a lack of
confidence in the system.
Security challenges arise if one state’s mDL system is less secure than
another’s, which could allow counterfeit or manipulated digital licenses to
pass unnoticed, leading to fraud or misuse. For the user experience,
travelers from one state may face difficulties if their mDL is not
recognized by systems in another state, such as at an airport or retail
store. This could result in delays, rejections, or the need to resort to
physical IDs, creating an inconvenient and inconsistent user experience.
## Digital Trust Service (DTS) [#digital-trust-service-dts]
The purpose of a **Digital Trust Service (DTS)** is to address this growing challenge by enabling
participants to rely on a single trusted framework. This simplifies interactions, as participants
need to establish trust only with the DTS itself rather than with each individual entity. It is the
network operator's responsibility to establish and maintain trust in different digital entities, and
then use a DTS to reflect that trust to all other participants.
This approach fosters a more scalable and cohesive network, where participants can confidently
engage in digital interactions, knowing that the DTS safeguards their interests. This enables
participants to enhance their operations, contributes to economic growth, and improves the user
experience, all while fostering a more secure and trusted digital environment.
DTSs enable scaling networks by incorporating **direct** and **proxy** trust relationships:
* **Direct trust**: In a direct trust model, participants establish trust relationships directly
with one another, as each party verifies and trusts the identity and security of the other
participant independently. This typically involves exchanging unique public keys directly
between participants:
A real-world analogy for this would be hosting a dinner party where every guest needs to personally
introduce themselves to every other guest to establish trust. Each person has to explain who they
are, verify their background, and remember everyone else’s information. While this can work in a
small group, it becomes increasingly challenging as the number of guests (or participants) grows.
Managing these direct trust relationships—like exchanging public keys—becomes complex and cumbersome
as every individual must retrieve, exchange, and manage multiple keys:
* **Proxy trust**: In contrast, a proxy trust model allows participants to rely on a third-party
intermediary, such as a DTS, to establish and manage trust on their behalf. Instead of verifying
other participants directly, entities trust the DTS to handle trust relationships. The DTS
operator manages all keys and links them to unique identifiers assigned to each participant:
This can be compared to the same dinner party but with a trusted host acting as an intermediary. The
host has already vetted and verified every guest, ensuring they are trustworthy. Guests no longer
need to introduce themselves to each other individually—they simply trust the host’s judgment. If
new guests arrive, the host ensures they are introduced to everyone, making interactions seamless
and scalable:
By leveraging proxy trust through a DTS, participants can benefit from a simpler, more scalable
trust framework. The DTS removes the need to establish individual trust relationships, streamlining
interactions and creating a secure and trusted digital environment.
**Examples of digital trust services**
The purpose of the DTS is to provide a unified set of standards and
regulations, enforced by the Financial Conduct Authority (FCA), so that
consumers can confidently use third-party applications to access their bank
accounts and financial data.
The DTS enables cross-border recognition of digital identities, so that a
citizen from Germany can use their national eID to securely access services
in France, without needing to establish trust with each individual service
provider in different countries.
The purpose of the DTS is to create a trusted digital identity system that
can be securely used across states and in a variety of in-person and remote
interactions.
## Trust framework [#trust-framework]
Trust networks are built on a legal foundation that establishes the rules and regulations governing
the network. These laws ensure compliance with data protection, security, and privacy requirements,
providing a trust framework for network participants to rely upon:
* **Policies**: Define how participants can operate and interact. Policies are expressed as
[trusted lists](/docs/digital-trust-service/trusted-lists): a trusted issuers list (who may issue),
a trusted readers list (who may request), and trusted wallets and technologies (which certified
applications may participate). See [Trusted Lists](/docs/digital-trust-service/trusted-lists) for a
full explanation of each category.
* **Recognized standards**: Defined and enforced set of recognized standards that ensure
uniformity and interoperability. These may include local standards such as the
[Trusted Digital Identity Framework (TDIF)](https://architecture.digital.gov.au/trusted-digital-identity-framework-tdif-0)
in Australia or the
[Digital Identity Services Trust Framework](https://www.digital.govt.nz/standards-and-guidance/identity/trust-framework)
in New Zealand, as well as global standards like
[ISO/IEC 18013-5](https://www.iso.org/standard/69084.html) for mobile driver’s licenses (mDLs).
* **Schemes and vocabularies**: Defined schemes and vocabularies for the content and structure of
supported verifiable credentials. These may include custom credential profiles created for the
trust network, as well as subsets of global or industry-specific standards.
**Examples of trust frameworks**
The trust framework includes certified applications, banks, and third-party
providers (TPPs). Consumers can trust apps that are approved by the Open
Banking Implementation Entity (OBIE) to securely access their financial
data, while banks and TPPs, regulated by the Financial Conduct Authority
(FCA), must follow strict data protection and strong customer authentication
protocols to ensure only authorized parties can access or initiate payments
on behalf of customers.
Trust is built through Qualified Trust Service Providers (QTSPs), national
eID systems, and cross-border electronic signatures. Citizens can trust
their national eID to securely access services across EU member states,
while electronic signatures and seals, issued by QTSPs, are legally
recognized and ensure secure transactions, such as signing contracts or
accessing government services.
The trust framework includes state-issued mobile driver's licenses (mDLs),
certified verification applications, and trusted verifiers such as law
enforcement and retailers. Only apps and verifiers adhering to the ISO/IEC
18013-5 standard can securely authenticate a user’s mDL, ensuring that
interactions like age verification or airport check-ins are both secure and
compliant with state and national standards.
## Operation [#operation]
The trust network operator acts as an *accreditation body*, responsible for overseeing the
accreditation process, assessing compliance, and granting certifications to qualifying participants.
This is based on an *accreditation framework*, a structured evaluation, ensuring all participants
meet predefined standards. *Onboarding and Offboarding mechanisms* facilitate the seamless entry and
exit of participants within the trust network, ensuring continuous adherence to standards and
policies.
Continuous monitoring and auditing processes should be in place to ensure that participants adhere
to the established policies and standards. This oversight helps maintain trust by detecting and
addressing potential issues or breaches.
**Examples for operating a DTS**
Operation is managed by the Open Banking Implementation Entity (OBIE), which
enforces compliance with the UK’s Open Banking Standard. This includes
overseeing the API ecosystem and certifying third-party providers that wish
to access bank customer data securely.
Operation is managed by national authorities that accredit Qualified Trust
Service Providers (QTSPs). These providers issue eID credentials, digital
signatures, and time stamps that are legally binding and recognized across
all EU member states.
State Departments of Motor Vehicles (DMVs) are responsible for issuing and
managing mobile driver's licenses (mDLs) through secure apps. The American
Association of Motor Vehicle Administrators (AAMVA) plays a central role in
setting standards and guidelines for mDL interoperability, ensuring that
mDLs are recognized across different states. AAMVA also helps certify and
authenticate verifiers and relying parties, such as law enforcement
agencies, retailers, and TSA, ensuring they meet the required security
standards to verify mDLs. This ensures that all parties, including
verifiers, are trusted to handle sensitive identity information securely and
consistently.
## Consuming [#consuming]
There are different models for a DTS to make the trust framework available for consumption. These
range from public and authenticated APIs that expose ecosystem policies to standardized mechanisms
such as VICAL for issuer trust lists and RICAL for reader trust lists. See
[DTS options](/docs/digital-trust-service/consuming-trust-overview) for the full menu and guidance on
choosing between them.
**Examples for consuming trust frameworks**
Consumers use third-party apps that connect to their bank accounts to view
financial data or make transactions. The apps rely on secure APIs and
customer authentication processes that ensure data is protected and
accessible only with the user’s consent.
Users consume trust services by using their national digital identity to
access public services or sign legal documents in another EU country.
Service providers verify these eIDs through the eIDAS trust framework,
ensuring they meet the required standards for cross-border transactions.
Citizens use their mDLs to verify their identity in various scenarios, such
as when boarding a flight or purchasing age-restricted products. The
verification process involves scanning a QR code or using an NFC reader to
ensure the mDL is authentic and has not been tampered with.
## Trust marks [#trust-marks]
Retrieved information can be displayed to participants using **trust marks**. These are digital
indicators that signify the security, authenticity, and trustworthiness of online interactions,
services, or entities.
Trust marks can take the form of digital badges, seals, or certificates that are prominently
displayed on websites, applications, or digital credentials. They provide assurance to users that
the entity displaying the trust mark has been verified and complies with stringent security
standards and best practices.
When identifying a trust mark, users gain confidence that their interactions are secure and that
their data is being handled responsibly. In essence, trust marks act as visual endorsements of
trustworthiness, making it easier for users to recognize and choose secure and reputable services.
**Examples for trust marks**
Consumers can see trust information when they connect to third-party apps.
These apps can display trust marks, such as the FCA logo or Open Banking
certification, indicating that they are certified and authorized to securely
access the user's financial data. This gives users confidence that their
data is being shared with a trusted party.
Users can view trust information when they sign documents or authenticate
themselves online. For example, when a citizen uses their eID to sign a
contract or access a cross-border service, they can be shown trust marks or
seals from a Qualified Trust Service Provider (QTSP). These marks indicate
that the transaction is legally recognized and secure across all EU member
states.
Trust information is displayed when users present their mDL for
verification. At a checkpoint, such as at a store or airport, the
verification system can show a confirmation screen or a trust mark
indicating that the verifier (e.g., TSA or retailer) is certified to accept
and authenticate mDLs according to the ISO/IEC 18013-5 standard. This
provides assurance that the user's digital ID is being handled securely by a
trusted party.
## MATTR capabilities [#mattr-capabilities]
MATTR platforms enable customers to effectively operate trust networks by offering the following key
features:
* **Network operation**: MATTR VII
[ecosystem capabilities](/docs/digital-trust-service) allow network operators to:
* Onboard entities as participants, and associate each participant with a unique
[IACA](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca) or
[DID](/docs/concepts/dids) to identify them.
* Configure valid credential types, and associate each credential type with a unique
combination of `format` and `docType`.
* Create a policy that defines what what *credential types* (identified by their **format**
and **type/docType**) can be **issued** and/or **verified** by what *participants*
(identified by their **IACA/DID**).
* Make this information available for issuers, holders and verifiers to consume and integrate
into their solutions using either:
* [MATTR VII APIs](/docs/api-reference/platform/policy/getLatestEcosystemPolicy).
* MATTR Pi [Holder](/docs/holding/sdk-overview), [Verifier Web](/docs/verification/remote-web-verifiers/sdks/overview) and [Verifier Mobile](/docs/verification/remote-mobile-verifiers/sdks/overview) SDKs.
* MATTR [GO Hold](/docs/holding/go-hold/getting-started) and/or
[GO Verify](/docs/verification/go-verify/getting-started).
* A [VICAL](/docs/digital-trust-service/vical-overview).
* **Credential issuance**: MATTR VII tenants can be used to [issue](/docs/issuance) and
manage verifiable credentials. When a MATTR VII tenant is onboarded into a
network, holders and verifiers that trust the trust network can also trust credentials issued by
this tenant without establishing a direct trust relationship with it.
* **Credential holding**: [MATTR Pi](/docs/holding/sdk-overview) and
[MATTR GO](/docs/holding/go-hold/getting-started) platforms enable creating digital
applications that allow users to claim, store, and present credentials. These platforms can
consume and display information from DTSs to enable holders easily establish trust with issuers
and verifiers they are interacting with.
* **Credential verification**: All MATTR platforms offer credential
[verification capabilities](/docs/verification). Once a verification application consumes trust
information from a DTS, verification processes are greatly simplified, as establishing trust
with every single issuer is delegated to the DTS operator.
## Example use cases [#example-use-cases]
National network that connect banks, fintech companies, and payment processors across the country in a unified, trusted network.
* Network operation (MATTR VII): MATTR VII is used to manage the trust network, onboarding banks, fintech providers, and payment processors. By leveraging the DTS, participants onboarding processes are simplified, ensuring all entities meet strict security and compliance standards.
* Credential issuance (MATTR VII): When a financial institution is onboarded into the network, it can use MATTR VII to issue trusted digital credentials, such as loan approvals, account statements, or credit scores. Since all participants trust the trust framework, there’s no need to establish individual relationships between issuers and verifiers, making the process faster and more efficient.
* Credential holding (MATTR Pi/GO): Service providers can offer customers MATTR Pi or GO wallets to store and manage their digital financial credentials. A key feature is that these wallets notify users when an accredited verifier—such as a bank, loan provider, or financial advisor—requests access to a specific credential. This transparency builds trust between users and verifiers, ensuring that users only share their financial information with trusted entities.
* Credential verification (MATTR VII/Pi/GO): Service providers use different MATTR verification capabilities to authenticate digital credentials, such as loan approvals or bank statements. The DTS centralizes trust management, so verification is fast and seamless.
These capabilities significantly enhance the security of financial transactions by ensuring that all participants adhere to strict standards for credential issuance and verification. Customers are notified when accredited verifiers request access to their credentials, giving them full control and confidence in how their sensitive financial information is shared. This transparency, combined with the streamlined processes, improves the overall user experience, reducing friction in financial interactions and encouraging more frequent use of digital services. As a result, higher customer engagement translates to increased transaction volumes and revenue growth for banks and fintech companies, while the improved security reduces the risk of fraud and data breaches.
Government-led initiatives aimed at issuing and managing digital identities for citizens, allowing them to securely access public services such as healthcare, education, and social benefits.
* Network operation (MATTR VII): MATTR VII enables onboarding government agencies, service providers, and private organizations into the trust network, allowing them to issue and verify digital identity credentials. The streamlined onboarding process ensures that all participants meet strict security standards, building a trusted network where users can confidently access public and private services.
* Credential issuance (MATTR VII): Government agencies use MATTR VII to issue digital credentials like national ID cards, driving licenses, or health insurance numbers. Because the trust is governed by the DTS, service providers and citizens trust these credentials without needing to establish direct relationships with each agency.
* Credential holding (MATTR Pi/GO): Government agencies can offer citizens MATTR Pi or GO wallets to securely store their digital credentials. These wallets enhance user trust by notifying citizens when an accredited verifier – such as a healthcare provider, government office, or private organization – requests access to their credentials. This transparency ensures that citizens feel in control of their personal data, only sharing it with trusted entities.
* Credential verification (MATTR platforms): Government services and private organizations use different MATTR verification capabilities to authenticate digital credentials. Because the DTS manages trust, verifiers can quickly and easily authenticate credentials, improving service delivery times.
This trust network enhances the security of digital identities by centralizing trust management and ensuring that credentials are issued and verified within a secure trust network. Citizens benefit from the transparency of being notified when accredited verifiers request access to their digital IDs, which protects them from unauthorized access and misuse of their personal information. The improved security fosters trust in the system, encouraging wider adoption of digital IDs and more frequent interactions between citizens and government services. This, in turn, leads to cost savings and operational efficiency for the government, while improving the overall delivery of public services.
Consortiums of international airlines, airports, and transportation authorities seeking to streamline identity verification for travelers, replacing physical documents like passports and boarding passes with secure digital credentials.
* Network operation (MATTR VII): MATTR VII is used to onboard airlines, airports, and border agencies into the trust network. The DTS ensures that participants adhere to the consortium's requirements, creating a seamless and secure experience for travelers.
* Credential issuance (MATTR VII): Airlines and border control authorities issue trusted travel credentials—such as digital boarding passes and visas—using MATTR VII. The DTS ensures that these credentials are trusted across the network, so travelers can move through checkpoints more quickly and securely.
* Credential holding (MATTR Pi/GO): Service providers can offer customers MATTR Pi or GO wallets to store their digital travel documents. These wallets not only store credentials but also notify travelers when an accredited verifier, such as a customs officer, airline, or airport security checkpoint, requests access to a document like a boarding pass or visa. This transparency ensures that travelers know who is requesting their data, fostering trust and confidence in the digital travel process.
* Credential verification (MATTR platforms): Airports, airlines, and customs agencies use different MATTR verification capabilities to authenticate travel credentials. The DTS handles all trust management, simplifying the verification process and enabling fast, secure checks at checkpoints.
This ensures that all digital travel credentials are issued and verified securely, significantly improving the security of the travel process. The improved security, along with the transparency of credential management, enhances traveler confidence and encourages the adoption of digital travel documents. This leads to faster processing at airports and borders, increasing passenger throughput and driving revenue growth for airlines and airports, while also strengthening security in global travel operations.
Global shipping authorities are responsible for verifying shipping documentation, such as certificates of origin, compliance documents, and customs declarations, across international trade routes.
* Network operation (MATTR VII): MATTR VII enables onboarding customs authorities, ports, and logistics companies into the trust network. The DTS governs the issuance and verification of shipping documents, ensuring all parties adhere to standardized, trusted processes. The improved trust framework leads to more efficient supply chain operations.
* Credential issuance (MATTR VII): Customs authorities and shipping companies issue trusted verifiable credentials (e.g., customs declarations, compliance certificates) using MATTR VII. Since these credentials are backed by the DTS, they are trusted across all participants in the supply chain, without requiring direct relationships between issuers and verifiers.
* Credential holding (MATTR Pi/GO): Service providers can offer shipping companies and logistics operators MATTR Pi or GO wallets to store and manage their shipping documents. These wallets notify users when an accredited verifier—such as a customs officer or port authority—requests access to a specific shipping document. This transparency reassures shipping operators that only trusted entities can access their documents, improving trust and efficiency in document management.
* Credential verification (MATTR platforms): Customs officers and port authorities use different MATTR verification capabilities to authenticate shipping credentials. The DTS handles the trust framework, ensuring that verifiers can instantly confirm the validity of documents without needing to manually validate each issuer. This streamlines customs clearance and port operations, allowing for faster shipment processing.
These capabilities improve the security of shipping operations by ensuring that all documents are issued and verified within a trusted and secure framework. This reduces the risk of unauthorized document access or tampering, leading to more secure and reliable operations. As a result, the enhanced security, along with faster processing times, increases the number of shipments that can be handled, driving revenue growth for logistics companies and port operators, while also reducing the risk of fraud or regulatory non-compliance.
## Underlying platforms [#underlying-platforms]
MATTR DTS capabilities are configured and operated using MATTR VII, either via the MATTR Portal or via API requests.
# Privacy in digital trust services
URL: /docs/digital-trust-service/privacy
Description: How a Digital Trust Service publishes trust information without observing individual verifications, and how that preserves the privacy properties of a decentralized credential ecosystem.
A [Digital Trust Service](/docs/digital-trust-service) (DTS) lets the operator of a trust network
publish information that participants need to evaluate trust: which issuers are accredited, which
verifiers can request which credentials, and what credential types are recognized in the
ecosystem.
This page describes the privacy properties of a DTS, why it does not introduce a central point of
surveillance, and how MATTR's DTS capability is designed to preserve the decentralized model.
For the broader picture, see [Privacy in MATTR's architecture](/docs/concepts/privacy).
## A DTS is a publication mechanism, not a transaction broker [#a-dts-is-a-publication-mechanism-not-a-transaction-broker]
The most important privacy property of a DTS is the role it plays in the architecture. A DTS:
* Publishes trust information so that issuers, holders, and verifiers can evaluate trust locally.
* Does not sit on the transaction path between holder and verifier.
* Does not see individual presentations.
This is what makes proxy trust (where every participant trusts the DTS) compatible with the
decentralized model. The DTS replaces the manual work of negotiating bilateral trust between
every pair of participants, without becoming an intermediary in every verification interaction.
Compare this to a federated identity provider, where the intermediary has to see every
authentication request in order to issue an assertion. A DTS does not have that property. Once a
verifier has fetched the trust list, it can verify credentials without ever talking to the DTS
again.
## What is in a trust list [#what-is-in-a-trust-list]
Trust lists published by a DTS contain entries that identify participants and the role they play
in the ecosystem. Typical contents include:
* Trusted issuer entries (often anchored to an
[IACA](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca)).
* Trusted verifier entries, where the ecosystem requires accredited verifiers.
* Recognized credential types and their associated formats.
* Cryptographic signatures by the DTS operator over the published lists, so that consumers can
verify the lists' integrity.
Trust lists do not contain personal information about credential holders. They are about the
participants in the trust network, not about the people whose data flows through the network.
For ecosystems that consume trust information as a VICAL (defined in
[ISO/IEC 18013-5](https://www.iso.org/standard/69084.html)), the VICAL is similarly a list of
trusted issuer certificate authorities, signed by the VICAL operator. It does not contain holder
data.
## How participants consume trust information [#how-participants-consume-trust-information]
MATTR's DTS capability supports several consumption patterns. In all of them, the consumer
fetches trust information independently of any specific verification:
* **MATTR VII APIs:** The trust network operator publishes ecosystem policies that
define participants and the credential types they can issue or verify. Issuers, holders, and
verifiers can fetch these policies via API.
* **MATTR Pi and MATTR GO SDK integrations:** Holder and verifier SDKs can consume ecosystem
information and apply it to the local presentation flow.
* **VICAL:** For ecosystems that follow the ISO/IEC 18013-5 model, the operator can publish a
signed VICAL that relying parties consume on a schedule.
In every pattern, the fetch is asynchronous and is not associated with a specific verification.
The DTS sees that some participant fetched a list. It does not see what verification activity, if
any, that fetch was related to.
## What MATTR's DTS capability does NOT do [#what-mattrs-dts-capability-does-not-do]
The MATTR VII DTS capability is designed as a publication mechanism. It explicitly does not:
* Receive verification events from participants.
* Maintain a per-holder ledger of credentials or presentations.
* Sit in the transaction path between holder and verifier at presentation time.
If a customer operating a DTS chooses to layer additional ecosystem-level analytics or reporting
on top, that is a separate system that the operator is responsible for designing and disclosing.
The core DTS capability does not introduce a central surveillance point.
## Trust frameworks and the rules around DTS operation [#trust-frameworks-and-the-rules-around-dts-operation]
A DTS does not operate in a vacuum. It is the technical expression of a trust framework: the
governance rules that define how participants are accredited, what claims credentials may carry,
and what obligations sit on issuers, holders, and verifiers.
Many trust frameworks explicitly prohibit issuers, wallets, and trust framework providers from
tracking or correlating verification activity. Where this applies, the DTS operator's job is to
design the service so that compliance is enforced architecturally rather than depending on each
participant's good behavior. MATTR's DTS capability supports this by keeping the DTS out of the
verification path.
See [Decentralized trust model](/docs/concepts/decentralized-trust-model) for a fuller treatment
of how governance instruments need to change as the architecture changes.
## Practical guidance for DTS operators [#practical-guidance-for-dts-operators]
If you are operating a DTS with MATTR VII, the following principles help preserve the
architecture's privacy properties:
* Publish trust information in a form that is consumable without identifying the consumer.
Authenticated APIs are fine, but the authentication should identify the consuming organization,
not individual transactions.
* Sign published lists. Consumers should be able to verify the integrity of the trust information
independently of the transport.
* Define a clear policy for what the DTS may and may not log about consumption. Most operators
benefit from aggregate consumption metrics; per-participant transactional logs are rarely
needed and easily abused.
* Make trust framework obligations explicit. Issuers, holders, and verifiers should know what
they are signing up to when they join the ecosystem.
* Choose the regional MATTR deployment that matches the data residency expectations of the
ecosystem you are operating.
## Frequently asked questions [#frequently-asked-questions]
### Does a Digital Trust Service see individual credential presentations? [#does-a-digital-trust-service-see-individual-credential-presentations]
No. A DTS publishes trust information (lists of trusted issuers, verifiers, and credential types)
that participants consume independently. It is not on the transaction path between holder and
verifier, and it does not see who is presenting what to whom.
### What does a trust list contain? [#what-does-a-trust-list-contain]
A trust list contains entries that identify participants in the trust network (typically by IACA,
DID, or signer certificate) and the credential types they are authorized to issue or verify. It
does not contain personal information about credential holders.
### Why use a DTS instead of direct trust relationships? [#why-use-a-dts-instead-of-direct-trust-relationships]
Direct trust relationships are workable in small ecosystems but do not scale. A DTS lets every
participant rely on a single trust anchor maintained by the network operator, rather than
negotiating bilateral trust with every other participant. This keeps the architecture
decentralized without requiring every party to vet every other party.
### Can a DTS be used to log verifications across an ecosystem? [#can-a-dts-be-used-to-log-verifications-across-an-ecosystem]
No. MATTR's DTS capability is designed as a publication mechanism and does not receive
verification events from participants. Ecosystem-level verification logging would require a
separate reporting or analytics system layered outside the DTS, which the customer would be
responsible for designing and disclosing. Doing so is also typically prohibited by the trust
framework rules that govern the DTS, as many trust frameworks explicitly prevent the DTS from
collecting verification activity.
### How do verifiers consume trust information from a MATTR DTS? [#how-do-verifiers-consume-trust-information-from-a-mattr-dts]
Trust information can be consumed via the MATTR VII Ecosystem APIs, via MATTR Pi and MATTR GO SDK
integrations, or as a VICAL (a signed Verified Issuer Certificate Authority List defined in
ISO/IEC 18013-5). The consumption is asynchronous and does not identify the verification activity
that triggered it.
## Related reading [#related-reading]
* [Privacy in MATTR's architecture](/docs/concepts/privacy)
* [Decentralized trust model](/docs/concepts/decentralized-trust-model)
* [Digital Trust Service overview](/docs/digital-trust-service)
* [VICAL](/docs/digital-trust-service/vical-overview)
* [Chain of trust](/docs/concepts/chain-of-trust)
# How to consume a RICAL as a wallet provider
URL: /docs/digital-trust-service/rical-consumption
Once a RICAL provider has published a RICAL, wallets need to retrieve it, verify its authenticity,
and load the trusted Reader Root Certificates into their verification logic. The steps below describe
how to do that against a RICAL published by a MATTR-hosted ecosystem.
ISO/IEC 18013-5 uses the term **reader** for the entity that requests data from an mDoc. Throughout
this documentation we use **verifier** or **relying party** for the same role. Certificate names
defined by the standard (for example, *Reader Root Certificate*) keep their ISO terminology.
### Retrieve the RICAL provider's root CA certificate [#retrieve-the-rical-providers-root-ca-certificate]
The RICAL provider and the VICAL provider share the same root CA distribution endpoint. Call the
public DTS root CA certificates endpoint to obtain the root certificate(s) used by the RICAL provider
as the anchor of its signing chain. The endpoint is public and does not require authentication.
```bash
curl https://your-tenant.vii.au01.mattr.global/v1/ecosystems/public/certificates/ca
```
This endpoint is public and does not require authentication. It should be provided by the RICAL
provider to all wallets in the ecosystem.
The response includes one or more PEM-encoded root certificates. Persist these certificates as your
trust anchor for the RICAL provider. They only need to be refreshed when the provider rotates its
root.
### Retrieve the latest RICAL [#retrieve-the-latest-rical]
Call the [Retrieve latest RICAL](/docs/api-reference/platform/rical/getLatestRical) endpoint to
download the current RICAL.
```bash
curl -o rical-latest.cbor \
https://your-tenant.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/ricals/public/latest
```
This endpoint is public and does not require authentication. It should be provided by the RICAL
provider to all wallets in the ecosystem.
The response is a binary CBOR file (`application/cbor`) encoded as a
[COSE\_Sign1](https://datatracker.ietf.org/doc/html/rfc9052) structure, following the same packaging
conventions as a VICAL.
Wallets should poll the latest endpoint on a schedule that matches the RICAL provider's publishing
cadence so they always operate against the latest set of trusted verifiers.
### Validate the RICAL signature and certificate chain [#validate-the-rical-signature-and-certificate-chain]
The RICAL is signed by a RICAL Signer certificate, which itself chains back to the RICAL provider's
root CA retrieved in step 1. Before trusting any data in the RICAL, perform the following checks:
1. Extract the RICAL Signer certificate from the COSE\_Sign1 unprotected header (`x5chain`,
COSE label `33`).
2. Verify the COSE\_Sign1 signature using the public key from that signer certificate and the
signature algorithm declared in the protected header (for example, ES256 / COSE algorithm `-7`).
3. Build the certificate chain from the signer certificate up to the root CA from step 1, and
validate each link: signature, validity period, key usage, and revocation status (CRL).
4. Confirm the root of the chain matches one of the trust-anchor certificates you persisted in
step 1.
If any of these checks fail, reject the RICAL and continue using the previously trusted version.
### Decode the RICAL payload [#decode-the-rical-payload]
Once the signature has been verified, decode the COSE\_Sign1 payload to access the RICAL data. The
decoded structure follows the CDDL definition in the
[RICAL overview](/docs/digital-trust-service/rical-overview#rical-structure). The `certificateInfos` array
contains one entry per trusted Reader Root Certificate, each carrying the DER-encoded certificate, its
serial number, and its Subject Key Identifier.
### Load the trusted Reader Root Certificates into your wallet [#load-the-trusted-reader-root-certificates-into-your-wallet]
Iterate over `payload.certificateInfos` and extract each `certificate`. These are the trust anchors
you should use when validating signed verifier requests:
```javascript
import { decode } from "cbor-x";
const ricalBytes = await fetch(
"https://your-tenant.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/ricals/public/latest"
).then((r) => r.arrayBuffer());
// COSE_Sign1 structure: [protected, unprotected, payload, signature]
const coseSign1 = decode(new Uint8Array(ricalBytes));
const payload = decode(coseSign1[2]);
const trustedReaderRoots = payload.certificateInfos.map((info) => ({
certificateDer: info.certificate,
serialNumber: info.serialNumber,
ski: info.ski,
}));
```
When evaluating a verifier request:
1. Extract the verifier's certificate chain from the signed request.
2. Build the chain up to a Reader Root Certificate and confirm that root matches one of the trusted
Reader Root Certificates loaded from the RICAL (for example, by comparing SKI or by direct
certificate match).
3. Validate the full PKI chain from the verifier's leaf certificate up to that trusted root,
including validity periods, key usage, and revocation status.
This mechanism lets a wallet authenticate verifiers across the ecosystem without maintaining
individual trust relationships with each relying party.
# Learn how to set up a RICAL
URL: /docs/digital-trust-service/rical-guide
## Introduction [#introduction]
The purpose of a [Reader Identity Certificate Authority List (RICAL)](/docs/digital-trust-service/rical-overview)
is to enable holder applications (wallets) in a digital ecosystem to verify the identity of relying
parties (verifiers) from a single authoritative source. It is the counterpart to a
[VICAL](/docs/digital-trust-service/vical-overview): where a VICAL distributes trusted *issuer*
roots, a RICAL distributes trusted *verifier* roots.
ISO/IEC 18013-5 uses the term **reader** for the entity that requests data from an mDoc. Throughout
this documentation we use **verifier** or **relying party** for the same role. Certificate names
defined by the standard (for example, *Reader Root Certificate*) keep their ISO terminology.
This guide will walk you through setting up a RICAL and publishing a list of trusted Reader Root
Certificates. Each step can be completed either through the
[Portal](/docs/platform-management/portal) or via the MATTR VII API. Select the tab that matches
how you want to work.
## Prerequisites [#prerequisites]
* Make sure you understand the concepts of a [RICAL](/docs/digital-trust-service/rical-overview) and how it
relates to a [Digital Trust Service (DTS)](/docs/digital-trust-service).
* You need access to an existing MATTR VII tenant with either the `DTS Provider` or `Admin` role.
Refer to the [Getting started with the Portal](/docs/platform-management/portal#getting-started)
tutorial to learn how to create a tenant and assign roles.
* To use the API, you also need to [obtain a bearer access token](/docs/api-reference#obtain-an-access-token)
to include in the `Authorization` header of each request.
## Guide overview [#guide-overview]
Publishing a RICAL comprises the following steps:
1. [Create an Ecosystem](#create-an-ecosystem): This is the overarching entity that holds
participants together. Only required if you don't already have an ecosystem on your tenant.
2. [Create participants and add verifier certificates](#create-participants-and-add-verifier-certificates):
These are the relying parties (verifiers) that will be part of the RICAL. Each participant includes
a Reader Root Certificate that anchors its verifier requests.
3. [Set up the RICAL signing certificate chain](#set-up-the-rical-signing-certificate-chain):
Establish the DTS root CA and RICAL Signer Certificate used to sign the RICAL, using either a
2-tier or 3-tier model.
4. [Manually publish a RICAL](#manually-publish-a-rical): Configure the RICAL provider, then generate
and publish a RICAL that includes your trusted Reader Root Certificates.
5. [Configure RICAL auto-generation and publishing](#configure-rical-auto-generation-and-publishing-optional)
(optional): Set the RICAL to automatically generate and publish on a daily or weekly schedule.
6. [View previously published RICALs](#view-previously-published-ricals-optional) (optional): Review
the history of previously published RICALs and download their files.
### Create an Ecosystem [#create-an-ecosystem]
Creating an Ecosystem is a one-time step per tenant. It is only required if you don't already
have an ecosystem on your tenant. If you already have an ecosystem (for example, because you
created one when setting up a [VICAL](/docs/digital-trust-service/vical-guide)), you will not see
the option to create a new one (in the Portal) and should skip directly to the next step. A
single ecosystem is shared across both your VICAL and RICAL.
Perform the following steps to create an Ecosystem:
1. Log in to the [MATTR Portal](https://portal.mattr.global/).
2. Navigate to the **Ecosystem** page under the *Digital Trust Service* section.
3. Enter a name for your Ecosystem, such as "My Digital Trust Service".
4. Select the **Create** button.
Make a request of the following structure to
[create an ecosystem](/docs/api-reference/platform/ecosystems/createEcosystem):
```http filename:"Request"
POST /v1/ecosystems
```
```json filename:"Request body"
{
"name": "My Digital Trust Service"
}
```
* `name` : This required parameter is the name used to identify your ecosystem.
The response will include an `id` property, which is the unique identifier for the ecosystem. You
will use this `ecosystemId` in subsequent requests to reference this ecosystem.
### Create participants and add verifier certificates [#create-participants-and-add-verifier-certificates]
Participants in a RICAL are the relying parties (verifiers) whose Reader Root Certificates you want
holders to trust. For each participant, you upload a Reader Root Certificate (referred to as a
**Verifier CA** in MATTR VII) that anchors the chain used to sign that verifier's requests.
Unlike an issuer participant in a VICAL, a verifier participant in a RICAL does **not** declare
credential types (`docTypes`). A trusted Reader Root Certificate is treated as authoritative for the
ecosystem rather than scoped to specific credential types. Authorization of what a verifier may
request is conveyed separately through the verifier's signed request and the holder's consent flow.
Perform the following steps to create a participant and add its verifier certificate:
1. Select the **Participants** page under the *Digital Trust Service* section (this page is only
visible if you have an existing ecosystem. If you don't have an ecosystem, you will need to return
to step 1 above and create it first).
2. Select the **Create new** button.\
The *Create participant* form appears, starting from Step 1 (*Details*).
3. Insert a meaningful *Name* for the participant (e.g. "Montcliff Police").
4. Use the *Country* dropdown list to select the participant's country (optional). When selected,
this value must match the *Country* value in the verifier certificate associated with this
participant.
5. If you select a country, a *State or Province* dropdown list is displayed. You can use it to
select the participant's state or province (optional). When selected, this value must match the
`stateOrProvinceName` value in the verifier certificate associated with this participant.
6. Insert the participant's *Address* and *Phone number* (optional).
7. Use the *Status* radio button to set the participant as **Active**.
8. Select the **Next** button.\
You are directed to Step 2 (*Certificates*).
9. Select the **Create** button to create the participant.
10. Select the **Verifier certificates** tab.
11. Select the **Add new** button to add a verifier certificate for the participant.\
The *Add verifier certificate* form appears.
12. Paste/upload the PEM-encoded Reader Root Certificate into the **Certificate PEM file** field.
13. Use the *Status* radio button to set the certificate to **Active**.
14. *(Optional)* To maintain trust continuity when rotating a participant's Reader Root Certificate,
expand the **Link Certificate** section and add this certificate as a successor to a previously
uploaded certificate (refer to
[Linked certificates](/docs/digital-trust-service/rical-overview#linked-certificates)):
* Use the *Predecessor certificate* dropdown to select the previous certificate that this new
one succeeds. The dropdown lists the participant's existing certificates because the
predecessor must already exist on your tenant before you can link to it.
* Upload the link certificate into the *Link Certificate PEM file* field. The link certificate
is the participant's proof that they own both the predecessor and the new certificate, which
is what lets you accept the new one while preserving trust in the previous one.
This option is only available once at least one certificate has been added for the participant.
15. Select the **Add** button.
Repeat the above steps for each relying party (verifier) you want to include in your RICAL.
First, make a request of the following structure to
[create a participant](/docs/api-reference/platform/participants/createEcosystemParticipant) and mark
it as a verifier:
```http filename:"Request"
POST /v1/ecosystems/{ecosystemId}/participants
```
* `ecosystemId` : Replace with the `id` value obtained when you created the ecosystem.
```json filename:"Request body"
{
"name": "Montcliff Police",
"status": "Active",
"isVerifier": true,
"country": "US",
"stateOrProvince": "US-XX",
"identifiers": {}
}
```
* `name` : This required parameter is a meaningful name used to identify the participant.
* `status` : Set this to `Active` so the participant is included in the ecosystem policy and the
RICAL. Only active participants are published.
* `isVerifier` : Set this to `true` so the participant can act as a verifier in the ecosystem.
* `country` / `stateOrProvince` : *Optional*. When provided, must match the corresponding values in
the verifier certificate uploaded for this participant.
* `identifiers` : This required parameter defines the participant's credential-format identifiers.
For a verifier participant, pass an empty object (`{}`), as the Reader Root Certificate is added
separately in the next request.
The response includes the participant `id`. Then, make a request of the following structure to
[create a verifier certificate](/docs/api-reference/platform/participants-verifier-certificates/createParticipantVerifierCertificate)
for the participant. This is the Reader Root Certificate that will be included in the RICAL:
```http filename:"Request"
POST /v1/ecosystems/{ecosystemId}/participants/{participantId}/verifier-certificates
```
* `participantId` : Replace with the `id` value obtained when you created the participant.
```json filename:"Request body"
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICTDCCAfKgAwIBAgIKeKcjIBGvXfS/sjAKBggqhkjOPQQDAjAwMQswCQYDVQQG\r\nEwJVUzEhMB8GA1UEAwwYRXhhbXBsZSBWZXJpZmllciByb290IENBMB4XDTI1MDgy\r\nNzIxMzMxNFoXDTMwMDgyNjIxMzMxNFowMDELMAkGA1UEBhMCVVMxITAfBgNVBAMM\r\nGEV4YW1wbGUgVmVyaWZpZXIgcm9vdCBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEH\r\nA0IABNJHM5ZE+fpVn7b9WwjVBiOiZq9eNXq1JkNj/6ZLe+2GkaRY/WE2Xbg7yx++\r\nh3QEdX3sGKzGO7dygQALBe/4qEyjgfMwgfAwHQYDVR0OBBYEFK8ogqdUH2vZlC1y\r\nNf619a8fnx8KMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMHsG\r\nA1UdHwR0MHIwcKBuoGyGamh0dHBzOi8vbGVhcm4udmlpLmF1MDEubWF0dHIuZ2xv\r\nYmFsL3YyL3ByZXNlbnRhdGlvbnMvY2VydGlmaWNhdGVzL2Q3YzE3ODI4LThkMTgt\r\nNDYyZS1iNDk3LWNjNjI2NWM4ZmQxYi9jcmwwLgYDVR0SBCcwJYYjaHR0cHM6Ly9s\r\nZWFybi52aWkuYXUwMS5tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSAAwRQIhAOqD\r\n0DF3rohBitl5jAj6x1164uGGj6yAhF/eE4aJeGc+AiAgaUYHzobzaPEWd+jZOh/A\r\nq8WgVJ+8sLx9WdJDs9/shQ==\r\n-----END CERTIFICATE-----\r\n",
"status": "Active"
}
```
* `certificatePem` : This required parameter contains the PEM-encoded Reader Root Certificate. It
becomes immutable once the certificate is created.
* `status` : Set to `Active` to include this certificate in the RICAL.
Note that, unlike an issuer certificate in a VICAL, a verifier certificate does not take a `docTypes`
array. Repeat this request for each relying party (verifier) you want to include in your RICAL.
### Set up the RICAL signing certificate chain [#set-up-the-rical-signing-certificate-chain]
Each RICAL must be signed by a RICAL Signer Certificate (RSC) that chains back to a DTS root CA via a
[chain of trust](/docs/digital-trust-service/certificates-overview). This chain is what consuming
wallets use as the trust anchor to validate the authenticity and integrity of the RICAL.
If you already have an existing active DTS root CA that you want to use as the trust anchor for your
RICAL, you can skip this step.
MATTR VII supports both **managed** and **unmanaged (external)** DTS certificates. Select the option
that matches how you want to manage your certificate infrastructure.
With managed DTS certificates, MATTR VII provisions and maintains the DTS root CA and the signer
certificates for you. You create and activate the DTS root CA, and MATTR VII automatically creates a
signer (and its certificate) and uses it to sign trust lists as required. Managed DTS certificates
always use the [2-tier model](/docs/digital-trust-service/certificates-overview#certificate-models-2-tier-and-3-tier).
1. Navigate to the **Certificates** page under the *Platform Management* section.
2. Select the **Create new** button.\
The *New certificate* form appears.
3. Use the *Type* dropdown list to select **DTS CA**.
4. Use the *Management method* radio button to select **MATTR managed**.
5. Enter a meaningful name in the *Organization* field to identify the organization operating the
DTS.
6. Use the *Country* dropdown list to select the country where the organization is located.
7. Select the **Create** button.\
The DTS root CA is created in an inactive state.
8. Scroll down and use the *Status* radio button to select **Active**.
9. Select the **Update** button to activate the DTS root CA.
Make a request of the following structure to
[create a managed DTS root CA](/docs/api-reference/platform/dts-root-ca-certificates/createDtsCaCertificate):
```http filename:"Request"
POST /v1/ecosystems/certificates/ca
```
```json filename:"Request body"
{
"organisationName": "Example Inc.",
"commonName": "Example DTS CA",
"country": "US"
}
```
* `organisationName` : This required parameter indicates the organization associated with the DTS
root CA certificate.
* `commonName` : This *optional* parameter indicates the common name of the DTS root CA certificate.
If not provided and a [custom domain](/docs/platform-management/custom-domain-overview) is
configured and verified, the custom domain is used followed by the words `DTS CA`. If no custom
domain is configured, the tenant subdomain is used instead.
* `country` : This *optional* parameter indicates the DTS provider's country. If not provided, a
country is selected based on the region of the tenant subdomain cloud host. When specified, the
value must be a valid [Alpha 2 country code](https://www.iso.org/glossary-for-iso-3166.html) as per
[ISO 3166-1](https://www.iso.org/standard/72482.html).
The response will include an `id` property identifying the managed DTS root CA. Make a request of the
following structure to
[update the managed DTS root CA](/docs/api-reference/platform/dts-root-ca-certificates/updateDtsCaCertificate)
and activate it:
```http filename:"Request"
PUT /v1/ecosystems/certificates/ca/{dtsCaCertificateId}
```
* `dtsCaCertificateId` : Replace with the `id` value obtained when you created the managed DTS root
CA.
```json filename:"Request body"
{
"active": true
}
```
Once a managed DTS root CA is activated, MATTR VII automatically creates and uses a signer to sign
trust lists as required.
With unmanaged (external) DTS certificates, you supply and maintain the full certificate chain. You
generate the DTS root CA, issue and sign the RICAL Signer Certificates (RSCs), upload the root and
each RSC to MATTR VII, and handle renewal and revocation.
Select the tab for the certificate model you want to configure. For guidance on choosing a model, see
[certificate models: 2-tier and 3-tier](/docs/digital-trust-service/certificates-overview#certificate-models-2-tier-and-3-tier).
In the 2-tier model, the DTS root CA directly signs the RICAL Signer Certificate (RSC).
**Generate a self-signed root certificate (DTS root CA)**
Use your preferred cryptographic library or tool to generate a self-signed root certificate (DTS
root CA). In the 2-tier model, this certificate directly signs the signer certificate. Ensure it
meets the requirements specified in [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html)
and in the [certificate requirements](/docs/digital-trust-service/certificates-overview#certificate-requirements)
section.
When using unmanaged (external) certificates, the DTS provider assumes full responsibility for the
secure management of the uploaded root certificates and all subordinate certificates. This includes
ensuring the protection, proper issuance, and timely revocation of certificates under the uploaded
root, as MATTR VII does not manage or monitor these certificates on the DTS provider's behalf.
**Register the external DTS root CA certificate with MATTR VII**
1. Expand the **Platform Management** menu in the navigation panel on the left-hand side.
2. Click on **Certificates**.
3. Select **Create new**.
4. Use the *Type* dropdown to select **DTS CA**.
5. Use the *Management method* dropdown to select **Externally managed**.
6. Paste/upload the PEM-encoded DTS root CA certificate into the **Certificate PEM file** field.\
The certificate must meet the following requirements:
* Valid
* Not expired
* Compliant with [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html)
7. Select **Create** to register the unmanaged DTS root CA certificate.
The newly created unmanaged DTS root CA is created in an inactive state. You can only activate it
after you create at least one signer associated with this DTS root CA.
Make a request of the following structure to
[create an unmanaged DTS root CA](/docs/api-reference/platform/dts-root-ca-certificates/createDtsCaCertificate):
```http filename:"Request"
POST /v1/ecosystems/certificates/ca
```
```json filename:"Request body"
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICDjCCAbSgAwIBAgIKdeZsA5NPKimuAzAKBggqhkjOPQQDAjAiMSAwCQYDVQQG\r\n...\r\n-----END CERTIFICATE-----\r\n"
}
```
* `certificatePem` : This required parameter contains the PEM-encoded DTS root CA certificate. The
certificate must meet the following requirements:
* Valid
* Not expired
* Compliant with [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html)
The response will include an `id` property, which is a unique identifier for the unmanaged DTS root
CA. This identifier will be used in subsequent operations to reference this unmanaged DTS root CA.
**Create a RICAL Signer**
Create a RICAL Signer under the DTS root CA. In the 2-tier model, the RICAL Signer references the DTS
root CA directly.
1. On the detail page of the DTS root CA you just registered, scroll down to the **RSC - RICAL Signer
Certificate** section and select **Add new**.
2. Select the **Create** button.\
MATTR VII creates a RICAL Signer in a *pending* state and generates a Certificate Signing Request
(CSR) for it.
Make a request of the following structure to
[create a RICAL signer](/docs/api-reference/platform/rical-signers/createRicalSigner) that references
the unmanaged DTS root CA:
```http filename:"Request"
POST /v1/ecosystems/certificates/rical-signers
```
```json filename:"Request body"
{
"caId": "080c670a-2e90-4023-b79f-b706e55e9bc6"
}
```
* `caId` : Replace with the `id` of the unmanaged DTS root CA. The response includes the RICAL signer
`id` and a `csrPem` (the Certificate Signing Request).
**Generate and sign the RICAL Signer Certificate (RSC)**
1. Use the **Download** or **Copy** buttons in the **Step 1. Download the RSC Certificate Signing
Request (CSR)** section of the RICAL Signer detail page to obtain the CSR.
2. Using your preferred cryptographic tool, generate and sign the RSC using the CSR from the previous
step. In the 2-tier model, the RSC must be signed by the DTS root CA's private key.
**Associate the RSC with the RICAL Signer**
Upload the signed RSC to the RICAL Signer and activate it.
1. On the RICAL Signer detail page, under **Step 2. Upload signed RSC**, paste/upload the
PEM-encoded RSC into the **Certificate PEM file** field.
2. Use the *Status* radio button to set the RICAL Signer to **Active**.
3. Select **Update** to associate the RSC and activate the RICAL Signer.
Make a request of the following structure to
[update the RICAL signer](/docs/api-reference/platform/rical-signers/updateRicalSigner) to associate
the signed RSC and activate it:
```http filename:"Request"
PUT /v1/ecosystems/certificates/rical-signers/{ricalSignerId}
```
* `ricalSignerId` : Replace with the RICAL signer `id` from the previous step.
```json filename:"Request body"
{
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIDXTCCAkWgAwIBAgIJAL5...\r\n-----END CERTIFICATE-----\r\n"
}
```
* `active` : Set to `true`. A RICAL signer can only be activated once a valid `certificatePem` is
provided.
* `certificatePem` : The PEM-encoded RSC generated from the CSR.
**Activate the DTS root CA**
1. Navigate back to the **Certificates** page in the MATTR Portal.
2. Select the DTS root CA you created in the first step.
3. Use the *Status* radio button to set the DTS root CA to **Active**.
4. Select **Update** to activate the DTS root CA.
Make a request of the following structure to
[update the unmanaged DTS root CA](/docs/api-reference/platform/dts-root-ca-certificates/updateDtsCaCertificate)
and activate it:
```http filename:"Request"
PUT /v1/ecosystems/certificates/ca/{dtsCaCertificateId}
```
* `dtsCaCertificateId` : Replace with the `id` value obtained when you registered the unmanaged DTS
root CA.
```json filename:"Request body"
{
"active": true
}
```
In the 3-tier model, a DTS intermediate CA sits between the DTS root CA and the RICAL Signer
Certificate (RSC). The DTS root CA signs the intermediate CA, and the intermediate CA signs the RSC.
**Generate a self-signed root certificate (DTS root CA)**
Use your preferred cryptographic library or tool to generate a self-signed root certificate (DTS
root CA). In the 3-tier model, this certificate will be used to sign the DTS intermediate CA
certificate (rather than signing the signer certificate directly). Ensure it meets the requirements
specified in [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html) and in the
[DTS root CA specific requirements](/docs/digital-trust-service/certificates-overview#dts-root-ca-specific-requirements)
section.
When using unmanaged (external) certificates, the DTS provider assumes full responsibility for the
secure management of the uploaded root certificates and all subordinate certificates. This includes
ensuring the protection, proper issuance, and timely revocation of certificates under the uploaded
root, as MATTR VII does not manage or monitor these certificates on the DTS provider's behalf.
**Register the external DTS root CA certificate with MATTR VII**
Register the DTS root CA and enable the 3-tier model so it requires an intermediate CA in its chain
of trust.
1. Expand the **Platform Management** menu in the navigation panel on the left-hand side.
2. Click on **Certificates**.
3. Select **Create new**.
4. Use the *Type* dropdown to select **DTS CA**.
5. Use the *Management method* dropdown to select **Externally managed**.
6. Paste/upload the PEM-encoded DTS root CA certificate into the **Certificate PEM file** field.\
The certificate must meet the following requirements:
* Valid
* Not expired
* Compliant with [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html)
7. Under **Certificate chain**, select **Three-tier with Intermediate CAs**. This configures the DTS
root CA to sign an intermediate CA rather than signing the signer certificate directly.
8. Select **Create** to register the unmanaged DTS root CA certificate.
The newly created unmanaged DTS root CA is created in an inactive state. You can only activate it
after you create a DTS intermediate CA and at least one signer associated with it.
Make a request of the following structure to
[create an unmanaged DTS root CA](/docs/api-reference/platform/dts-root-ca-certificates/createDtsCaCertificate).
To enable the 3-tier model, set `useIntermediateCa` to `true`:
```http filename:"Request"
POST /v1/ecosystems/certificates/ca
```
```json filename:"Request body"
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICDjCCAbSgAwIBAgIKdeZsA5NPKimuAzAKBggqhkjOPQQDAjAiMSAwCQYDVQQG\r\n...\r\n-----END CERTIFICATE-----\r\n",
"useIntermediateCa": true
}
```
* `certificatePem` : This required parameter contains the PEM-encoded DTS root CA certificate. The
certificate must meet the following requirements:
* Valid
* Not expired
* Compliant with [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html)
* `useIntermediateCa` : Set this to `true` to require and use intermediate CA certificates as part
of this DTS root CA's chain of trust. This field can only be set for unmanaged (external) DTS root
CA certificates. Changing this value later requires deleting all subordinate certificates.
The response will include an `id` property, which is a unique identifier for the unmanaged DTS root
CA. This identifier will be used in subsequent operations to reference this unmanaged DTS root CA.
**Generate and sign the DTS intermediate CA certificate**
Use your preferred cryptographic library or tool to generate a DTS intermediate CA certificate and
sign it with the DTS root CA private key. Ensure it meets the
[DTS intermediate CA specific requirements](/docs/digital-trust-service/certificates-overview#dts-intermediate-ca-specific-requirements),
including matching the country of the DTS root CA and remaining within the DTS root CA's validity
period.
Unlike the signer certificate, MATTR VII does not issue a Certificate Signing Request (CSR) for the
intermediate CA. You generate the intermediate CA's key pair and sign its certificate entirely
within your own PKI, then upload the finished certificate in the next step.
**Register the DTS intermediate CA certificate with MATTR VII**
Register the signed intermediate CA certificate under the DTS root CA you created in the previous
step.
1. Scroll down to the *Child certificates* section.
2. In the **Intermediate CA** section, select **Add new**.
3. Paste/upload the PEM-encoded DTS intermediate CA certificate into the **Certificate PEM file**
field.
4. Under **Allowed child certificates**, select the signer types this intermediate CA is allowed to
sign:
* **VSC (VICAL Signer Certificate)** to sign a VICAL Signer.
* **RSC (RICAL Signer Certificate)** to sign a RICAL Signer.
Select both if the intermediate CA will sign both signer types.
5. Select **Create** to register the DTS intermediate CA certificate.
Make a request of the following structure to
[create a DTS intermediate CA certificate](/docs/api-reference/platform/dts-intermediate-ca-certificates/createDtsIntermediateCaCertificate)
under the DTS root CA:
```http filename:"Request"
POST /v1/ecosystems/certificates/ca/{dtsCaCertificateId}/intermediate
```
* `dtsCaCertificateId` : Replace with the `id` value obtained when you registered the unmanaged DTS
root CA in the previous step.
```json filename:"Request body"
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICDjCCAbSgAwIBAgIKdeZsA5NPKimuAzAKBggqhkjOPQQDAjAiMSAwCQYDVQQG\r\n...\r\n-----END CERTIFICATE-----\r\n",
"usages": ["VICAL", "RICAL"]
}
```
* `certificatePem` : This required parameter contains the PEM-encoded DTS intermediate CA
certificate, signed by the DTS root CA.
* `usages` : This required parameter specifies the intended usages for this intermediate CA. It must
contain at least one value (`VICAL`, `RICAL`). Include the list type whose signer will chain to
this intermediate CA (use `VICAL` for a VICAL Signer, `RICAL` for a RICAL Signer, or both if the
intermediate CA will sign both).
The response will include an `id` property, which is the unique identifier for the DTS intermediate
CA certificate. You will use this identifier when creating the signer.
**Create a RICAL Signer**
Create a RICAL Signer under the DTS intermediate CA. In the 3-tier model, the RICAL Signer
references the intermediate CA rather than the DTS root CA.
1. Navigate to the **Certificates** page and select the DTS root CA, then select the DTS
intermediate CA you registered in the previous step.
2. In the **RSC – RICAL Signer Certificate** section, select **Add new**.\
MATTR VII creates a RICAL Signer in a *pending* state and generates a Certificate Signing Request
(CSR) for it.
Make a request of the following structure to
[create a RICAL signer](/docs/api-reference/platform/rical-signers/createRicalSigner) that references
the DTS intermediate CA:
```http filename:"Request"
POST /v1/ecosystems/certificates/rical-signers
```
```json filename:"Request body"
{
"intermediateCaId": "c1bbe671-21f8-5358-9537-eed4669b43f3"
}
```
* `intermediateCaId` : Replace with the `id` of the DTS intermediate CA. In the 3-tier model, the
RICAL signer references the intermediate CA rather than the DTS root CA. The response includes the
RICAL signer `id` and a `csrPem`.
**Generate and sign the RICAL Signer Certificate (RSC)**
1. Use the **Download** or **Copy** buttons in the **Step 1. Download the RSC Certificate Signing
Request (CSR)** section of the RICAL Signer detail page to obtain the CSR.
2. Using your preferred cryptographic tool, generate and sign the RSC using the CSR from the previous
step. In the 3-tier model, the RSC must be signed by the DTS intermediate CA's private key.
**Associate the RSC with the RICAL Signer**
Upload the signed RSC to the RICAL Signer and activate it.
1. On the RICAL Signer detail page, under **Step 2. Upload signed RSC**, paste/upload the
PEM-encoded RSC into the **Certificate PEM file** field.
2. Use the *Status* radio button to set the RICAL Signer to **Active**.
3. Select **Update** to associate the RSC and activate the RICAL Signer.
Make a request of the following structure to
[update the RICAL signer](/docs/api-reference/platform/rical-signers/updateRicalSigner) to associate
the signed RSC and activate it:
```http filename:"Request"
PUT /v1/ecosystems/certificates/rical-signers/{ricalSignerId}
```
* `ricalSignerId` : Replace with the RICAL signer `id` from the previous step.
```json filename:"Request body"
{
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIDXTCCAkWgAwIBAgIJAL5...\r\n-----END CERTIFICATE-----\r\n"
}
```
* `active` : Set to `true`. A RICAL signer can only be activated once a valid `certificatePem` is
provided.
* `certificatePem` : The PEM-encoded RSC generated from the CSR.
**Activate the DTS root CA**
1. Navigate back to the **Certificates** page in the MATTR Portal.
2. Select the DTS root CA you created in the first step.
3. Use the *Status* radio button to set the DTS root CA to **Active**.
4. Select **Update** to activate the DTS root CA.
Make a request of the following structure to
[update the unmanaged DTS root CA](/docs/api-reference/platform/dts-root-ca-certificates/updateDtsCaCertificate)
and activate it:
```http filename:"Request"
PUT /v1/ecosystems/certificates/ca/{dtsCaCertificateId}
```
* `dtsCaCertificateId` : Replace with the `id` value obtained when you registered the unmanaged DTS
root CA.
```json filename:"Request body"
{
"active": true
}
```
### Manually Publish a RICAL [#manually-publish-a-rical]
After you have created your participants and set up the signing certificate chain, you can publish a
RICAL that includes the trusted Reader Root Certificates. When you publish the RICAL, MATTR VII signs
a list that includes the information you provided for each Reader Root Certificate.
1. Navigate to the **Trust lists** page under the *Digital Trust Service* section.
2. Select the **RICAL (Trusted verifiers)** tab.
3. Enter a meaningful *Provider name* to identify the provider of the RICAL. This is included in the
RICAL metadata and used by wallets to identify the source of the RICAL.
4. Select the **Create** button.
5. Review the preview area where you can see all Reader Root Certificates included in the RICAL.
6. Select **Generate & Publish** when you are ready.\
The RICAL is now generated and published, and a modal is displayed where you can:
* Use the **Download** button to download the RICAL file (CBOR).
* Use the **Copy** button to copy a link to the public endpoint where wallets can access the RICAL.
First, make a request of the following structure to
[update the RICAL configuration](/docs/api-reference/platform/rical-configuration/updateRicalConfiguration)
and set the RICAL provider name. A RICAL configuration is required before a RICAL can be created:
```http filename:"Request"
PUT /v1/ecosystems/{ecosystemId}/ricals/configuration
```
* `ecosystemId` : Replace with the `id` value of your ecosystem.
```json filename:"Request body"
{
"ricalProvider": "Example Provider"
}
```
* `ricalProvider` : This required parameter is the provider name included in the RICAL metadata and
used by wallets to identify the source of the RICAL.
Then, make a request of the following structure to
[create (generate and publish) a RICAL](/docs/api-reference/platform/rical/createRical) based on your
ecosystem policy:
```http filename:"Request"
POST /v1/ecosystems/{ecosystemId}/ricals
```
The response includes the `ricalIssueID` and issuance `date` of the published RICAL. Wallets can
retrieve it from the public
[Retrieve latest RICAL](/docs/api-reference/platform/rical/getLatestRical) endpoint:
```bash
curl -o rical-latest.cbor \
https://your-tenant.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/ricals/public/latest
```
### Configure RICAL auto-generation and publishing (optional) [#configure-rical-auto-generation-and-publishing-optional]
You can optionally set up auto-generation of your RICAL so it is generated and published on a
schedule.
1. Return to the **Trust lists** page under the *Digital Trust Service* section.
2. Select the **RICAL (Trusted verifiers)** tab.
3. Expand the *RICAL configuration* panel.
4. Use the *Generation method* radio button to select **Auto generate**.
5. Use the *Auto generate frequency* dropdown list to select how often you want the RICAL to be
automatically generated and published (daily/weekly).
6. Select the **Update** button.
7. Review the preview area where you can see all Reader Root Certificates included in the RICAL.
Note that the RICAL is not generated and published yet. It will only be generated and published
automatically based on the frequency you selected in step 5 above. If you want to generate and
publish the RICAL immediately, you can select the **Generate & Publish** button.
Make a request of the following structure to
[update the RICAL configuration](/docs/api-reference/platform/rical-configuration/updateRicalConfiguration)
and enable scheduled auto-publishing:
```http filename:"Request"
PUT /v1/ecosystems/{ecosystemId}/ricals/configuration
```
```json filename:"Request body"
{
"ricalProvider": "Example Provider",
"autoPublish": {
"enabled": true,
"frequency": "Daily"
}
}
```
* `autoPublish.enabled` : Set to `true` to enable scheduled automatic generation and publishing of
the RICAL.
* `autoPublish.frequency` : Required when `enabled` is `true`. How often the RICAL is automatically
generated and published. One of `Daily` or `Weekly`.
When auto-publishing is enabled, the RICAL is generated and published automatically on the schedule
you set. You can still generate and publish a RICAL immediately at any time by calling the
[Create a RICAL](/docs/api-reference/platform/rical/createRical) endpoint.
### View Previously Published RICALs (optional) [#view-previously-published-ricals-optional]
1. Return to the **Trust lists** page under the *Digital Trust Service* section.
2. Select the **RICAL (Trusted verifiers)** tab.
3. Scroll down and select the **View Previously Published** button to open the *Previously generated
RICAL* view, which lists each RICAL with its *Issue ID*, *Generated at* time, *File name*,
*Trigger method*, and *Status*.
4. Use the **Download** button to download any previously published RICAL.
Make a request of the following structure to
[retrieve all RICALs](/docs/api-reference/platform/rical/getRicals) published in your ecosystem. This
endpoint is public and does not require authentication:
```http filename:"Request"
GET /v1/ecosystems/{ecosystemId}/ricals/public
```
The response is a JSON list of the published RICALs with their `ricalIssueID`, issuance `date`, and
`filename`. To download a specific RICAL file (a CBOR-encoded file), call the
[Retrieve specific RICAL](/docs/api-reference/platform/rical/getRical) endpoint with the relevant
`ricalIssueId`:
```bash
curl -o rical.cbor \
https://your-tenant.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/ricals/public/{ricalIssueId}
```
## Next steps [#next-steps]
Now that you have published your RICAL, you can share the public endpoint with wallet providers so
they can consume the RICAL and establish trust in the verifiers included in it. Refer to the
[RICAL consumption guide](/docs/digital-trust-service/rical-consumption) to learn how wallets can retrieve,
validate and use a RICAL.
# Overview
URL: /docs/digital-trust-service/rical-overview
## Introduction [#introduction]
A RICAL (Reader Identity Certificate Authority List) is a mechanism defined in
[ISO/IEC 18013-5](https://www.iso.org/standard/69084.html) Annex F to support establishing trust
in digital ecosystems where holders need to verify the identity of relying parties (verifiers)
requesting data from their mDocs.
ISO/IEC 18013-5 uses the term **reader** for the entity that requests data from an mDoc. Throughout
this documentation we use **verifier** or **relying party** for the same role. Certificate names
defined by the standard (for example, *Reader Root Certificate* and *Reader Authority Certificate*)
keep their ISO terminology.
It is the counterpart to a [VICAL](/docs/digital-trust-service/vical-overview): where a VICAL lets relying
parties consume issuer trust information from a single authoritative source, a RICAL lets
holder applications (wallets) consume verifier trust information in the same centralized way.
For example, consider the case of a holder presenting a Mobile Driver's License (mDL) to a verifier.
Different relying parties (law enforcement agencies, age verification services, financial
institutions) each operate their own Reader Authority Certificates. Without a RICAL, every wallet
would have to individually assess and trust each verifier's root certificate, which becomes
impractical as the number of verifiers in an ecosystem grows.
A RICAL solves this by collecting and validating Reader Root Certificates from different relying
parties, and then cryptographically signing them into a single list. When a wallet trusts a RICAL,
it can trust any verifier whose certificate chain anchors to a root included in the RICAL, without
maintaining a direct trust relationship with each individual verifier.
## RICAL roles [#rical-roles]
* **RICAL Provider**: Operates the RICAL and provides it as a service to ecosystem participants.
The RICAL provider collects and validates Reader Root Certificates from relying parties,
compiles them into a standardized RICAL format and distributes the result to holders.
* **Relying parties (verifiers)**: Request data from mDocs and present their verifier certificate so
holders can authenticate them.
* **Holders (Wallets)**: Consume the RICAL and use verifier information to verify the identity of
relying parties before releasing data.
## RICAL components [#rical-components]
* **RICAL metadata:** General information about the RICAL itself:
* Version.
* Provider.
* Issuance date.
* Unique identifier.
* **RICAL records:** Each record carries the information a wallet needs to establish trust in a
verifier's certificate chain:
* The DER-encoded trusted Reader Root Certificate.
* The certificate serial number.
* The Subject Key Identifier (SKI).
* The issuing country and (where applicable) state or province name.
* The DER-encoded issuer and subject distinguished names.
* The certificate validity period (`notBefore` and `notAfter`).
Unlike a VICAL, a RICAL record does **not** include a `docType` array. A trusted Reader Root
Certificate in a RICAL is treated as authoritative for the ecosystem rather than scoped to specific
credential types. Authorization of which data a verifier may request is conveyed separately through
the verifier's signed request and the holder's consent flow, not through the trust list itself.
## RICAL structure [#rical-structure]
The decoded RICAL payload is structurally similar to a VICAL, but each record describes a trusted
Reader Root Certificate rather than an issuer, and there is no per-record `docType` array:
```text
RICAL = {
"version" : tstr, ; RICAL structure version, currently "1.0"
"provider" : tstr, ; Identifies the RICAL provider
"date" : tdate, ; date-time of RICAL issuance
"id" : uint, ; Uniquely identifies this specific issue of the RICAL
"type" : tstr, ; RICAL type, currently "reader"
"certificateInfos" : [+ {
"certificate" : bstr, ; DER-encoded X.509 certificate
"serialNumber" : biguint, ; Serial number of the certificate
"ski" : bstr, ; Subject Key Identifier
"issuingCountry" : tstr, ; ISO 3166-1 alpha-2 country code
"stateOrProvinceName" : tstr, ; State or province (sub-national issuers)
"issuer" : bstr, ; DER-encoded issuer distinguished name
"subject" : bstr, ; DER-encoded subject distinguished name
"notBefore" : tdate, ; Certificate validity start
"notAfter" : tdate ; Certificate validity end
}]
}
```
## How it works [#how-it-works]
1. The RICAL provider establishes its own root certificate with an associated Public Key
Infrastructure (PKI) chain of certificates, based on the
[chain of trust](/docs/concepts/chain-of-trust) model.
2. The RICAL provider collects and validates Reader Root Certificates from different relying
parties. Each of these roots is vetted by the RICAL provider before inclusion.
3. The RICAL provider uses their chain of trust end-entity certificate to sign the validated
Reader Root Certificates into a single list.
4. Each relying party uses their own Reader Authority Certificate (and the associated PKI chain)
to sign verifier requests.
5. Holders can consume the RICAL in one of two ways:
* Download the RICAL directly from the provider's website.
* Retrieve the RICAL via an endpoint exposed by the provider as an API.
6. When a holder receives a request from a verifier, the wallet validates the verifier's signature
and referenced PKI certificate chain against the RICAL to ensure that the chain anchors to a
trusted Reader Root Certificate present in the list.
7. Upon successful validation, the wallet can confidently authenticate the relying party without
maintaining an individual trust relationship with each verifier.
## Linked certificates [#linked-certificates]
A **linked certificate** lets a participant rotate the Reader Root Certificate they use in the
ecosystem without breaking trust for holder applications (wallets) that already trust the previous
certificate. When you add a new certificate for a participant, you can add it as a **successor** to a
previously uploaded certificate by selecting the **predecessor** and uploading a **link certificate**
that ties the new certificate to the previous one.
This preserves trust continuity for the participant across the rotation, so wallets can continue to
authenticate the verifier without first having to consume an updated RICAL.
To add a linked certificate, refer to the [RICAL guide](/docs/digital-trust-service/rical-guide).
## Next steps [#next-steps]
* Follow the [RICAL guide](/docs/digital-trust-service/rical-guide) to set up a RICAL and publish a list of
trusted Reader Root Certificates, using either the Portal or the MATTR VII API.
* Refer to the [RICAL consumption guide](/docs/digital-trust-service/rical-consumption) to learn how wallets
can retrieve, validate and use a published RICAL.
# Trusted Lists
URL: /docs/digital-trust-service/trusted-lists
Description: The categories of trust a Digital Trust Service publishes (trusted issuers, trusted readers, and trusted wallets) and why each one matters.
A Digital Trust Service (DTS) makes trust decisions scalable by publishing **trusted lists**.
Instead of every participant establishing a direct relationship with every other participant, each
participant trusts the lists published by the network operator. Those lists answer three separate
questions.
* **Who is authorized to issue** a given credential type?
* **Who is authorized to read** (request) a given credential type?
* **Which wallets and technologies** are certified to participate?
Understanding these three categories first makes it easier to see where the specific mechanisms
[VICAL](/docs/digital-trust-service/vical-overview) and
[RICAL](/docs/digital-trust-service/rical-overview) fit in. See
[DTS options](/docs/digital-trust-service/consuming-trust-overview) for how MATTR lets you publish and
consume each category.
## Trusted Issuer Lists [#trusted-issuer-lists]
A Trusted Issuer List defines which issuers are authorized to issue specific types of verifiable
credentials. A verifier or wallet can then trust a credential without needing a direct relationship
with each issuer. It only needs to trust the list.
**Why it matters:** Maintaining an up-to-date relationship with every issuer across multiple
jurisdictions and credential types is operationally complex. Certificates rotate, new issuers come
online, and different authorities publish independently. A Trusted Issuer List delegates that work to
the network operator.
**Example:** A retailer verifying mobile driver's licenses (mDLs) needs to accept licenses from every
participating state. Rather than tracking each state's Issuing Authority Certificate Authority (IACA)
individually, the retailer's verifier trusts a single issuer list that the network operator keeps
current as new states come online.
**How MATTR implements it:** through ecosystem policies exposed via the
[MATTR VII APIs](/docs/api-reference/platform/policy/getLatestEcosystemPolicy) and through
[VICAL](/docs/digital-trust-service/vical-overview), the ISO/IEC 18013-5 standardized mechanism for
publishing issuer certificate authority lists. See
[DTS options](/docs/digital-trust-service/consuming-trust-overview) for how to choose between them.
## Trusted Reader Lists [#trusted-reader-lists]
A Trusted Reader List defines which verifiers (readers) are authorized to request specific types of
credentials. A holder or wallet can then trust a verifier before presenting data to it.
**Why it matters:** Holders should only disclose data to parties that are entitled to request it.
A Trusted Reader List gives wallets a way to recognize accredited verifiers and warn users about, or
block, requests from parties that are not on the list.
**Example:** A citizen's wallet receives a request for their date of birth. Before the wallet shows a
consent screen, it checks that the requesting verifier appears on the network's reader list. If the
verifier is recognized, the wallet can display a trust indicator. If it is not, the wallet can warn the
user or decline the request.
**How MATTR implements it:** through
[RICAL](/docs/digital-trust-service/rical-overview) (Reader Certificate Authority List), the mechanism
for publishing reader trust lists. Ecosystem policies exposed via the
[MATTR VII APIs](/docs/api-reference/platform/policy/getLatestEcosystemPolicy) can also express which
participants are permitted to verify a given credential type.
## Trusted Wallets and Trusted Technologies [#trusted-wallets-and-trusted-technologies]
A Trusted Technologies list defines which certified applications and solutions may participate in the
network. This can include digital wallets, identity agents, registers, and verifiable credentials that
meet established criteria for secure and reliable operation.
**Why it matters:** The integrity of a trust network depends on the software participants use. Trusting
only certified wallets and technologies keeps uncertified, and potentially malicious, applications out
of sensitive interactions.
**Example:** An issuer wants to ensure its credentials are only claimed by wallet applications it has
authorized. It requires wallets to cryptographically prove their authenticity before claiming, so only
recognized wallet instances can receive its credentials.
**How MATTR implements it:** through
[wallet attestation](/docs/issuance/credential-issuance/wallet-attestation), which validates a wallet
instance through a certificate-based trust chain before it can claim credentials, and through the
recognized standards and certification criteria defined by the network's
[trust framework](/docs/digital-trust-service#trust-framework).
## Next steps [#next-steps]
* [DTS options](/docs/digital-trust-service/consuming-trust-overview): the options MATTR offers for
publishing and consuming each category of trust.
* [VICAL overview](/docs/digital-trust-service/vical-overview): the standardized mechanism for issuer
trust lists.
* [RICAL overview](/docs/digital-trust-service/rical-overview): the mechanism for reader trust lists.
# How to consume a VICAL as a relying party
URL: /docs/digital-trust-service/vical-consumption
Once a VICAL provider has published a VICAL, relying parties need to retrieve it, verify its
authenticity, and load the trusted IACAs into their verification solution. The steps below describe
how to do that against a VICAL published by a MATTR-hosted ecosystem.
### Retrieve the VICAL provider's root CA certificate [#retrieve-the-vical-providers-root-ca-certificate]
Call the [Retrieve all public DTS root CA certificates](/docs/api-reference/platform/dts-root-ca-certificates/getDtsCaCertificatePublic)
endpoint to obtain the root certificate(s) used by the VICAL provider as the anchor of its signing
chain. The endpoint is public and does not require authentication.
```bash
curl https://your-tenant.vii.au01.mattr.global/v1/ecosystems/public/certificates/ca
```
This endpoint is public and does not require authentication. It should be provided by the VICAL
provider to all relying parties in the ecosystem.
The response includes one or more PEM-encoded root certificates:
```json
{
"rootCertificates": [
{
"certificate": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"notBefore": "2025-10-22T00:00:00Z",
"notAfter": "2030-10-22T00:00:00Z",
"fingerprint": "f6cad6e579d70b3973efa60624af731a580d1a11a7579e70f2f10f059dc86172",
"commonName": "Example VICAL Provider Root CA"
}
]
}
```
Persist these certificates as your trust anchor for the VICAL provider. They only need to be
refreshed when the provider rotates its root.
### Retrieve the latest VICAL [#retrieve-the-latest-vical]
Call the [Retrieve latest VICAL](/docs/digital-trust-service/vical-api-reference/general#retrieve-latest-vical)
endpoint to download the current VICAL.
```bash
curl -o vical-latest.cbor \
https://your-tenant.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/vicals/public/latest
```
This endpoint is public and does not require authentication. It should be provided by the VICAL
provider to all relying parties in the ecosystem.
The response is a binary CBOR file (`application/cbor`) encoded as a
[COSE\_Sign1](https://datatracker.ietf.org/doc/html/rfc9052) structure, as defined in
[ISO/IEC 18013-5](https://www.iso.org/standard/69084.html) Annex C.
Relying parties should poll this endpoint on a schedule that matches the VICAL provider's publishing
cadence so they always operate against the latest set of trusted issuers.
### Validate the VICAL signature and certificate chain [#validate-the-vical-signature-and-certificate-chain]
The VICAL is signed by a VICAL Signer certificate, which itself chains back to the VICAL provider's
root CA retrieved in step 1. Before trusting any data in the VICAL, perform the following checks:
1. Extract the VICAL Signer certificate from the COSE\_Sign1 unprotected header (`x5chain`,
COSE label `33`).
2. Verify the COSE\_Sign1 signature using the public key from that signer certificate and the
signature algorithm declared in the protected header (for example, ES256 / COSE algorithm `-7`).
3. Build the certificate chain from the signer certificate up to the root CA from step 1, and
validate each link: signature, validity period, key usage, and revocation status (CRL).
4. Confirm the root of the chain matches one of the trust-anchor certificates you persisted in
step 1.
If any of these checks fail, reject the VICAL and continue using the previously trusted version.
### Decode the VICAL payload [#decode-the-vical-payload]
Once the signature has been verified, decode the COSE\_Sign1 payload to access the VICAL data. The
decoded structure looks like this (values are illustrative):
```json
{
"protectedHeader": {
"1": -7
},
"unprotectedHeader": {
"33": {
"type": "X.509 Certificate",
"subject": "C=AU, CN=example-dts.vii.au01.mattr.global VICAL Signer, O=Example Provider",
"issuer": "C=AU, CN=Example Provider Root CA, O=Example Provider",
"validFrom": "2025-11-11T02:26:13.000Z",
"validTo": "2029-02-10T02:26:13.000Z",
"fingerprint": "68:58:45:41:5A:AF:10:3A:85:01:78:E2:40:C9:59:51:AB:9B:10:13",
"pem": "-----BEGIN CERTIFICATE-----\nMIICnzCCAkagAwIBAgIK...\n-----END CERTIFICATE-----"
}
},
"payload": {
"version": "1.0",
"vicalProvider": "Example Provider",
"date": "2025-11-15T20:59:01.000Z",
"vicalIssueID": 41,
"certificateInfos": [
{
"certificate": {
"type": "X.509 Certificate",
"subject": "CN=Example IACA, O=Example DLD, ST=US-XX, C=US",
"issuer": "CN=Example IACA, O=Example DLD, ST=US-XX, C=US",
"validFrom": "2024-01-01T00:00:00.000Z",
"validTo": "2034-01-01T00:00:00.000Z",
"pem": "-----BEGIN CERTIFICATE-----\nMIICeDCCAh+gAwIBAgIQ...\n-----END CERTIFICATE-----",
"isCertificateAuthority": true
},
"serialNumber": "5A5BB04A119A35796D2AD477D93A1ACC",
"ski": "3C:4C:6C:DF:E7:82:34:2E:E1:14:E6:CE:AD:12:0A:39:FD:08:34:6B",
"issuingCountry": "US",
"stateOrProvinceName": "US-XX",
"notBefore": "2024-01-01T00:00:00.000Z",
"notAfter": "2034-01-01T00:00:00.000Z",
"docType": ["org.iso.18013.5.1.mDL"],
"issuingAuthority": "CN=Example IACA, O=Example DLD, ST=US-XX, C=US"
}
]
}
}
```
The `payload.certificateInfos` array contains one entry per trusted IACA. Each entry includes the
PEM-encoded IACA, its validity period, the issuing authority's identifiers, and the array of
`docType` values it is authorized to sign.
### Load the trusted IACAs into your verification solution [#load-the-trusted-iacas-into-your-verification-solution]
Iterate over `payload.certificateInfos` and extract each `certificate`. In raw CBOR, `certificate`
is the DER-encoded X.509 IACA byte string (the JSON above is a rendered view). These IACAs are the
trust anchors you use when validating presented mDocs:
```javascript
import { decode } from "cbor-x";
const vicalBytes = await fetch(
"https://your-tenant.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/vicals/public/latest"
).then((r) => r.arrayBuffer());
// COSE_Sign1 structure: [protected, unprotected, payload, signature]
const coseSign1 = decode(new Uint8Array(vicalBytes));
const payload = decode(coseSign1[2]);
const trustAnchors = payload.certificateInfos.map((info) => ({
certificateDer: info.certificate,
issuingAuthority: info.issuingAuthority,
docTypes: info.docType,
validFrom: info.notBefore,
validTo: info.notAfter,
}));
```
When verifying a presented mDoc:
1. Look up the IACA referenced by the mDoc's Document Signer Certificate against your loaded trust
anchors.
2. Confirm the mDoc's `docType` is listed under that IACA's authorized `docType` array. If it is
not, reject the credential even when the signature chain is otherwise valid.
3. Validate the full PKI chain from the mDoc's signer up to the IACA as the trust anchor.
This mechanism lets you verify mDocs from any issuer represented in the VICAL without maintaining
individual trust relationships with each issuing authority.
# Learn how to set up a VICAL
URL: /docs/digital-trust-service/vical-guide
## Introduction [#introduction]
The purpose of a [Verified Issuer Certificate Authority List (VICAL)](/docs/digital-trust-service/vical-overview) is to enable different
participants in a digital ecosystem to rely on a single trusted framework.
This guide will walk you through setting up a VICAL and publishing a policy that
defines trusted participants and credential types. Each step can be completed either through the
[Portal](/docs/platform-management/portal) or via the MATTR VII API. Select the tab that matches
how you want to work.
## Prerequisites [#prerequisites]
* Make sure you understand the concepts of a [VICAL](/docs/digital-trust-service/vical-overview) and how it relates
to a [Digital Trust Service (DTS)](/docs/digital-trust-service).
* You need access to an existing MATTR VII tenant with either the `DTS Provider` or `Admin` role.
Refer to the [Getting started with the Portal](/docs/platform-management/portal#getting-started) tutorial
to learn how to create a tenant and assign roles.
* To use the API, you also need to [obtain a bearer access token](/docs/api-reference#obtain-an-access-token)
to include in the `Authorization` header of each request.
## Guide overview [#guide-overview]
Publishing a VICAL comprises the following steps:
1. [Create an Ecosystem](#create-an-ecosystem): This is the overarching entity that holds
participants and credential types together. Only required if you don't already have an ecosystem
on your tenant.
2. [Create participants and add issuer certificates](#create-participants-and-add-issuer-certificates):
These are the issuers that will be part of the VICAL. Each participant includes one or more IACA
certificates and the credential types they are allowed to issue.
3. [Set up the VICAL signing certificate chain](#set-up-the-vical-signing-certificate-chain):
Establish the DTS root CA and VICAL Signer Certificate used to sign the VICAL, using either a
2-tier or 3-tier model.
4. [Manually publish a VICAL](#manually-publish-a-vical): Configure the VICAL provider, then generate
and publish a VICAL that includes your participants and their credential types.
5. [Configure VICAL auto-generation and publishing](#configure-vical-auto-generation-and-publishing-optional)
(optional): Set the VICAL to automatically generate and publish on a daily or weekly schedule.
6. [View previously published VICALs](#view-previously-published-vicals-optional) (optional):
Review the history of previously published VICALs and download their policy files.
### Create an Ecosystem [#create-an-ecosystem]
Creating an Ecosystem is a one-time step per tenant. It is only required if you don't already
have an ecosystem on your tenant. If you already have an ecosystem (for example, because you
created one when setting up a [RICAL](/docs/digital-trust-service/rical-guide)), you will not see the
option to create a new one (in the Portal) and should skip directly to the next step. A single
ecosystem is shared across both your VICAL and RICAL.
Perform the following steps to create an Ecosystem:
1. Log in to the [MATTR Portal](https://portal.mattr.global/).
2. Navigate to the **Ecosystem** page under the *Digital Trust Service* section.
3. Enter a name for your Ecosystem, such as "My Digital Trust Service".
4. Select the **Create** button.
Make a request of the following structure to
[create an ecosystem](/docs/api-reference/platform/ecosystems/createEcosystem):
```http filename:"Request"
POST /v1/ecosystems
```
```json filename:"Request body"
{
"name": "My Digital Trust Service"
}
```
* `name` : This required parameter is the name used to identify your ecosystem.
The response will include an `id` property, which is the unique identifier for the ecosystem. You
will use this `ecosystemId` in subsequent requests to reference this ecosystem.
### Create participants and add issuer certificates [#create-participants-and-add-issuer-certificates]
Participants are entities that represent issuers that will be included in the VICAL. For each participant, you will need to provide one or more IACA certificates that will be used as the trust anchor when signing mDocs, and define what credential types they are allowed to issue.
Perform the following steps to create a participant:
1. Select the **Participants** page under the *Digital Trust Service* section (this page is only visible if you have an existing ecosystem. If you don't have an ecosystem, you will need to return to step 1 above and create it first).
2. Select the **Create new** button.\
The *Create participant* form appears, starting from Step 1 (*Details*).
3. Insert a meaningful *Name* for the participant (e.g. "Montcliff DMV").
4. Use the *Country* dropdown list to select the Participant’s country (optional). Note that when
selected, this value must match the *Country* value in the IACA certificate associated with this
participant.
5. If you select a country, a *State or Province* dropdown list is displayed. You can use it to
select the Participant’s state or province (optional). Note that when selected, this value must
match the *StateOrProvinceName* value in the IACA certificate associated with this participant.
6. Insert the participant’s *Address* (optional).
7. Insert the participant’s *Phone number* (optional).
8. Use the *Status* radio button to set the participant as **Active**.
9. Click the **Next** button.\
You are directed to Step 2 (*Certificates*).
10. Click the **Create** button to create the participant.
11. Select the **Issuer certificates** tab.
12. Select the **Add new** button to add an issuer certificate for the participant.\
The *Add issuer certificate* form appears.
13. Upload the PEM file you want to use as this participant’s identifier for issuing mDocs (this must be a
valid IACA certificate and match any values set for *Country* and *State or Province* above).\
You should now see the certificate summary and details.
14. Use the *Credential types valid for* field to select the credential types that this participant will be allowed to issue.
* You can insert as many credential types as you want.
* You can use the pre-populated options (`org.iso.18013.5.1.mDL` and/or `org.iso.23220.photoid.1`) or insert custom credential types that are relevant to your ecosystem.
* The credential type is just a string value and does not need to match any values in the certificate. It is only used to link the participant to the credential types they are allowed to issue.
15. Scroll down and use the *Status* dropdown list to set the certificate as ***Active***.
16. *(Optional)* To maintain trust continuity when rotating a participant's IACA certificate, expand
the **Link Certificate** section and add this certificate as a successor to a previously uploaded
certificate (refer to
[Linked certificates](/docs/digital-trust-service/vical-overview#linked-certificates)):
* Use the *Predecessor certificate* dropdown to select the previous certificate that this new
one succeeds. The dropdown lists the participant's existing certificates because the
predecessor must already exist on your tenant before you can link to it.
* Upload the link certificate into the *Link Certificate PEM file* field. The link certificate
is the participant's proof that they own both the predecessor and the new certificate, which
is what lets you accept the new one while preserving trust in the previous one.
This option is only available once at least one certificate has been added for the participant.
17. Click the **Add** button.
Repeat the above steps for each participant you want to include in your VICAL.
Make a request of the following structure to
[create a participant](/docs/api-reference/platform/participants/createEcosystemParticipant) in your
ecosystem. The participant's IACA certificate and its authorized credential types are provided as a
`mobile` identifier:
```http filename:"Request"
POST /v1/ecosystems/{ecosystemId}/participants
```
* `ecosystemId` : Replace with the `id` value obtained when you created the ecosystem.
```json filename:"Request body"
{
"name": "Montcliff DMV",
"status": "Active",
"isIssuer": true,
"country": "US",
"stateOrProvince": "US-XX",
"identifiers": {
"mobile": [
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\n...\r\n-----END CERTIFICATE-----\r\n",
"status": "Active",
"docTypes": ["org.iso.18013.5.1.mDL"]
}
]
}
}
```
* `name` : This required parameter is a meaningful name used to identify the participant.
* `status` : Set this to `Active` so the participant is included in the ecosystem policy and the
VICAL. Only active participants are published.
* `isIssuer` : Set this to `true` so the participant can act as an issuer in the ecosystem.
* `country` : This *optional* parameter is the participant's [Alpha 2 country code](https://www.iso.org/glossary-for-iso-3166.html).
When provided, it must match the `country` value in the IACA certificate.
* `stateOrProvince` : This *optional* parameter is the participant's [ISO 3166-2](https://www.iso.org/standard/72483.html)
state or province. When provided, it must match the `stateOrProvinceName` value in the IACA
certificate.
* `identifiers.mobile` : An array containing the participant's mDoc identifier(s). Each entry
includes:
* `certificatePem` : The PEM-encoded IACA certificate this participant uses as the trust anchor
when signing mDocs. It must be a valid IACA as defined in Annex B of
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
* `status` : Set to `Active` to include this certificate in the VICAL.
* `docTypes` : The credential types this participant is allowed to issue with this IACA. The
value is a string and does not need to match any value in the certificate.
The response will include an `id` property identifying the participant. Repeat this request for each
participant you want to include in your VICAL.
### Set up the VICAL signing certificate chain [#set-up-the-vical-signing-certificate-chain]
Each VICAL must be signed by a VICAL Signer Certificate (VSC) that chains back to a DTS root CA via a
[chain of trust](/docs/digital-trust-service/certificates-overview). This chain is what consuming
relying parties use as the trust anchor to validate the authenticity and integrity of the VICAL.
If you already have an existing active DTS root CA that you want to use as the trust anchor for your
VICAL, you can skip this step.
MATTR VII supports both **managed** and **unmanaged (external)** DTS certificates. Select the option
that matches how you want to manage your certificate infrastructure.
With managed DTS certificates, MATTR VII provisions and maintains the DTS root CA and the signer
certificates for you. You create and activate the DTS root CA, and MATTR VII automatically creates a
signer (and its certificate) and uses it to sign trust lists as required. Managed DTS certificates
always use the [2-tier model](/docs/digital-trust-service/certificates-overview#certificate-models-2-tier-and-3-tier).
1. Navigate to the **Certificates** page under the *Platform Management* section.
2. Select the **Create new** button.\
The *New certificate* form appears.
3. Use the *Type* dropdown list to select **DTS CA**.
4. Use the *Management method* radio button to select **MATTR managed**.
5. Enter a meaningful name in the *Organization* field to identify the organization operating the
DTS.
6. Use the *Country* dropdown list to select the country where the organization is located.
7. Select the **Create** button.\
The DTS root CA is created in an inactive state.
8. Scroll down and use the *Status* radio button to select **Active**.
9. Select the **Update** button to activate the DTS root CA.
Make a request of the following structure to
[create a managed DTS root CA](/docs/api-reference/platform/dts-root-ca-certificates/createDtsCaCertificate):
```http filename:"Request"
POST /v1/ecosystems/certificates/ca
```
```json filename:"Request body"
{
"organisationName": "Example Inc.",
"commonName": "Example DTS CA",
"country": "US"
}
```
* `organisationName` : This required parameter indicates the organization associated with the DTS
root CA certificate.
* `commonName` : This *optional* parameter indicates the common name of the DTS root CA certificate.
If not provided and a [custom domain](/docs/platform-management/custom-domain-overview) is
configured and verified, the custom domain is used followed by the words `DTS CA`. If no custom
domain is configured, the tenant subdomain is used instead.
* `country` : This *optional* parameter indicates the DTS provider's country. If not provided, a
country is selected based on the region of the tenant subdomain cloud host. When specified, the
value must be a valid [Alpha 2 country code](https://www.iso.org/glossary-for-iso-3166.html) as per
[ISO 3166-1](https://www.iso.org/standard/72482.html).
The response will include an `id` property identifying the managed DTS root CA. Make a request of the
following structure to
[update the managed DTS root CA](/docs/api-reference/platform/dts-root-ca-certificates/updateDtsCaCertificate)
and activate it:
```http filename:"Request"
PUT /v1/ecosystems/certificates/ca/{dtsCaCertificateId}
```
* `dtsCaCertificateId` : Replace with the `id` value obtained when you created the managed DTS root
CA.
```json filename:"Request body"
{
"active": true
}
```
Once a managed DTS root CA is activated, MATTR VII automatically creates and uses a signer to sign
trust lists as required.
With unmanaged (external) DTS certificates, you supply and maintain the full certificate chain. You
generate the DTS root CA, issue and sign the VICAL Signer Certificates (VSCs), upload the root and
each VSC to MATTR VII, and handle renewal and revocation.
Select the tab for the certificate model you want to configure. For guidance on choosing a model, see
[certificate models: 2-tier and 3-tier](/docs/digital-trust-service/certificates-overview#certificate-models-2-tier-and-3-tier).
In the 2-tier model, the DTS root CA certificate directly signs the VICAL Signer Certificate (VSC).
**Generate a self-signed root certificate (DTS root CA)**
Use your preferred cryptographic library or tool to generate a self-signed root certificate (DTS
root CA). In the 2-tier model, this certificate directly signs the signer certificate. Ensure it
meets the requirements specified in [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html)
and in the [certificate requirements](/docs/digital-trust-service/certificates-overview#certificate-requirements)
section.
When using unmanaged (external) certificates, the DTS provider assumes full responsibility for the
secure management of the uploaded root certificates and all subordinate certificates. This includes
ensuring the protection, proper issuance, and timely revocation of certificates under the uploaded
root, as MATTR VII does not manage or monitor these certificates on the DTS provider's behalf.
**Register the external DTS root CA certificate with MATTR VII**
1. Expand the **Platform Management** menu in the navigation panel on the left-hand side.
2. Click on **Certificates**.
3. Select **Create new**.
4. Use the *Type* dropdown to select **DTS CA**.
5. Use the *Management method* dropdown to select **Externally managed**.
6. Paste/upload the PEM-encoded DTS root CA certificate into the **Certificate PEM file** field.\
The certificate must meet the following requirements:
* Valid
* Not expired
* Compliant with [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html)
7. Select **Create** to register the unmanaged DTS root CA certificate.
The newly created unmanaged DTS root CA is created in an inactive state. You can only activate it
after you create at least one signer associated with this DTS root CA.
Make a request of the following structure to
[create an unmanaged DTS root CA](/docs/api-reference/platform/dts-root-ca-certificates/createDtsCaCertificate):
```http filename:"Request"
POST /v1/ecosystems/certificates/ca
```
```json filename:"Request body"
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICDjCCAbSgAwIBAgIKdeZsA5NPKimuAzAKBggqhkjOPQQDAjAiMSAwCQYDVQQG\r\n...\r\n-----END CERTIFICATE-----\r\n"
}
```
* `certificatePem` : This required parameter contains the PEM-encoded DTS root CA certificate. The
certificate must meet the following requirements:
* Valid
* Not expired
* Compliant with [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html)
The response will include an `id` property, which is a unique identifier for the unmanaged DTS root
CA. This identifier will be used in subsequent operations to reference this unmanaged DTS root CA.
**Create a VICAL Signer**
Create a VICAL Signer under the DTS root CA. In the 2-tier model, the VICAL Signer references the DTS
root CA directly.
1. On the detail page of the DTS root CA you just registered, scroll down to the **VSC – VICAL Signer
Certificate** section and select **Add new**.
2. Select the **Create** button.\
MATTR VII creates a VICAL Signer in a *pending* state and generates a Certificate Signing Request
(CSR) for it.
Make a request of the following structure to
[create a VICAL Signer](/docs/digital-trust-service/vical-api-reference/signers#create-a-vical-signer)
that references the unmanaged DTS root CA:
```http filename:"Request"
POST /v1/ecosystems/certificates/vical-signers
```
```json filename:"Request body"
{
"caId": "080c670a-2e90-4023-b79f-b706e55e9bc6"
}
```
* `caId` : Replace with the `id` value obtained when you created the unmanaged DTS root CA in the
previous step. Attempts to provide a managed DTS root CA identifier for manual VICAL Signer
creation will result in an error.
The response will include two properties which you will use later in this guide:
* `id` : The unique identifier for the VICAL Signer. This identifier will be used in subsequent
operations to reference this VICAL Signer.
* `csrPem` : The X.509 Certificate Signing Request (CSR) in PEM format. You will use this CSR to
generate a valid VICAL Signer Certificate (VSC) in the next step.
**Generate and sign the VICAL Signer Certificate (VSC)**
1. Use the **Download** or **Copy** buttons in the **Step 1. Download the VSC Certificate Signing
Request (CSR)** section of the VICAL Signer detail page to obtain the CSR.
2. Using your preferred cryptographic library or tool, generate and sign a VICAL Signer Certificate
(VSC) using the CSR from the previous step. In the 2-tier model, the VSC must be signed by the DTS
root CA's private key. Refer to the
[VSC specific requirements](/docs/digital-trust-service/certificates-overview#signer-certificate-specific-requirements-vsc-and-rsc)
section for details on how to structure a valid VSC.
**Associate the VSC with the VICAL Signer**
Upload the signed VSC to the VICAL Signer and activate it.
1. On the VICAL Signer detail page, under **Step 2. Upload signed VSC**, paste/upload the
PEM-encoded VSC into the **Certificate PEM file** field.
2. Use the *Status* radio button to set the VICAL Signer to **Active**.
3. Select **Update** to associate the VSC and activate the VICAL Signer.
Make a request of the following structure to
[update the VICAL Signer](/docs/digital-trust-service/vical-api-reference/signers#update-a-vical-signer)
to activate and associate it with the generated VSC:
```http filename:"Request"
PUT /v1/ecosystems/certificates/vical-signers/{vicalSignerId}
```
* `vicalSignerId` : Replace with the `id` value obtained when you created the VICAL Signer in the
previous step.
```json filename:"Request body"
{
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICbzCCAhSgAwIBAgIKfS7sskyJEh+DOzAKBggqhkjOPQQDAjAiMSAwCQYDVQQG\r\n...\r\n-----END CERTIFICATE-----\r\n"
}
```
* `active` : This required boolean indicates whether the VICAL Signer is active or not. Can only be
set to `true` when a `certificatePem` is provided. Only active VICAL Signers can be used to sign
VICALs.
* `certificatePem` : This required parameter contains the PEM-encoded VSC created in the previous
step.
**Activate the DTS root CA**
1. Navigate back to the **Certificates** page in the MATTR Portal.
2. Select the DTS root CA you created in the first step.
3. Use the *Status* radio button to set the DTS root CA to **Active**.
4. Select **Update** to activate the DTS root CA.
Make a request of the following structure to
[update the unmanaged DTS root CA](/docs/api-reference/platform/dts-root-ca-certificates/updateDtsCaCertificate)
and activate it:
```http filename:"Request"
PUT /v1/ecosystems/certificates/ca/{dtsCaCertificateId}
```
* `dtsCaCertificateId` : Replace with the `id` value obtained when you registered the unmanaged DTS
root CA.
```json filename:"Request body"
{
"active": true
}
```
In the 3-tier model, a DTS intermediate CA certificate sits between the DTS root CA and the VICAL
Signer Certificate (VSC). The DTS root CA signs the intermediate CA, and the intermediate CA signs
the VSC.
**Generate a self-signed root certificate (DTS root CA)**
Use your preferred cryptographic library or tool to generate a self-signed root certificate (DTS
root CA). In the 3-tier model, this certificate will be used to sign the DTS intermediate CA
certificate (rather than signing the signer certificate directly). Ensure it meets the requirements
specified in [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html) and in the
[DTS root CA specific requirements](/docs/digital-trust-service/certificates-overview#dts-root-ca-specific-requirements)
section.
When using unmanaged (external) certificates, the DTS provider assumes full responsibility for the
secure management of the uploaded root certificates and all subordinate certificates. This includes
ensuring the protection, proper issuance, and timely revocation of certificates under the uploaded
root, as MATTR VII does not manage or monitor these certificates on the DTS provider's behalf.
**Register the external DTS root CA certificate with MATTR VII**
Register the DTS root CA and enable the 3-tier model so it requires an intermediate CA in its chain
of trust.
1. Expand the **Platform Management** menu in the navigation panel on the left-hand side.
2. Click on **Certificates**.
3. Select **Create new**.
4. Use the *Type* dropdown to select **DTS CA**.
5. Use the *Management method* dropdown to select **Externally managed**.
6. Paste/upload the PEM-encoded DTS root CA certificate into the **Certificate PEM file** field.\
The certificate must meet the following requirements:
* Valid
* Not expired
* Compliant with [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html)
7. Under **Certificate chain**, select **Three-tier with Intermediate CAs**. This configures the DTS
root CA to sign an intermediate CA rather than signing the signer certificate directly.
8. Select **Create** to register the unmanaged DTS root CA certificate.
The newly created unmanaged DTS root CA is created in an inactive state. You can only activate it
after you create a DTS intermediate CA and at least one signer associated with it.
Make a request of the following structure to
[create an unmanaged DTS root CA](/docs/api-reference/platform/dts-root-ca-certificates/createDtsCaCertificate).
To enable the 3-tier model, set `useIntermediateCa` to `true`:
```http filename:"Request"
POST /v1/ecosystems/certificates/ca
```
```json filename:"Request body"
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICDjCCAbSgAwIBAgIKdeZsA5NPKimuAzAKBggqhkjOPQQDAjAiMSAwCQYDVQQG\r\n...\r\n-----END CERTIFICATE-----\r\n",
"useIntermediateCa": true
}
```
* `certificatePem` : This required parameter contains the PEM-encoded DTS root CA certificate. The
certificate must meet the following requirements:
* Valid
* Not expired
* Compliant with [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html)
* `useIntermediateCa` : Set this to `true` to require and use intermediate CA certificates as part
of this DTS root CA's chain of trust. This field can only be set for unmanaged (external) DTS root
CA certificates. Changing this value later requires deleting all subordinate certificates.
The response will include an `id` property, which is a unique identifier for the unmanaged DTS root
CA. This identifier will be used in subsequent operations to reference this unmanaged DTS root CA.
**Generate and sign the DTS intermediate CA certificate**
Use your preferred cryptographic library or tool to generate a DTS intermediate CA certificate and
sign it with the DTS root CA private key. Ensure it meets the
[DTS intermediate CA specific requirements](/docs/digital-trust-service/certificates-overview#dts-intermediate-ca-specific-requirements),
including matching the country of the DTS root CA and remaining within the DTS root CA's validity
period.
Unlike the signer certificate, MATTR VII does not issue a Certificate Signing Request (CSR) for the
intermediate CA. You generate the intermediate CA's key pair and sign its certificate entirely
within your own PKI, then upload the finished certificate in the next step.
**Register the DTS intermediate CA certificate with MATTR VII**
Register the signed intermediate CA certificate under the DTS root CA you created in the previous
step.
1. Scroll down to the *Child certificates* section.
2. In the **Intermediate CA** section, select **Add new**.
3. Paste/upload the PEM-encoded DTS intermediate CA certificate into the **Certificate PEM file**
field.
4. Under **Allowed child certificates**, select the signer types this intermediate CA is allowed to
sign:
* **VSC (VICAL Signer Certificate)** to sign a VICAL Signer.
* **RSC (RICAL Signer Certificate)** to sign a RICAL Signer.
Select both if the intermediate CA will sign both signer types.
5. Select **Create** to register the DTS intermediate CA certificate.
Make a request of the following structure to
[create a DTS intermediate CA certificate](/docs/api-reference/platform/dts-intermediate-ca-certificates/createDtsIntermediateCaCertificate)
under the DTS root CA:
```http filename:"Request"
POST /v1/ecosystems/certificates/ca/{dtsCaCertificateId}/intermediate
```
* `dtsCaCertificateId` : Replace with the `id` value obtained when you registered the unmanaged DTS
root CA in the previous step.
```json filename:"Request body"
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICDjCCAbSgAwIBAgIKdeZsA5NPKimuAzAKBggqhkjOPQQDAjAiMSAwCQYDVQQG\r\n...\r\n-----END CERTIFICATE-----\r\n",
"usages": ["VICAL", "RICAL"]
}
```
* `certificatePem` : This required parameter contains the PEM-encoded DTS intermediate CA
certificate, signed by the DTS root CA.
* `usages` : This required parameter specifies the intended usages for this intermediate CA. It must
contain at least one value (`VICAL`, `RICAL`). Include the list type whose signer will chain to
this intermediate CA (use `VICAL` for a VICAL Signer, `RICAL` for a RICAL Signer, or both if the
intermediate CA will sign both).
The response will include an `id` property, which is the unique identifier for the DTS intermediate
CA certificate. You will use this identifier when creating the signer.
**Create a VICAL Signer**
Create a VICAL Signer under the DTS intermediate CA. In the 3-tier model, the VICAL Signer
references the intermediate CA rather than the DTS root CA.
1. Navigate to the **Certificates** page and select the DTS root CA, then select the DTS
intermediate CA you registered in the previous step.
2. In the **VSC – VICAL Signer Certificate** section, select **Add new**.\
MATTR VII creates a VICAL Signer in a *pending* state and generates a Certificate Signing Request
(CSR) for it.
Make a request of the following structure to
[create a VICAL Signer](/docs/digital-trust-service/vical-api-reference/signers#create-a-vical-signer)
that references the DTS intermediate CA certificate:
```http filename:"Request"
POST /v1/ecosystems/certificates/vical-signers
```
```json filename:"Request body"
{
"intermediateCaId": "080c670a-2e90-4023-b79f-b706e55e9bc6"
}
```
* `intermediateCaId` : Replace with the `id` value obtained when you registered the DTS intermediate
CA certificate. In the 3-tier model, the VICAL Signer references the intermediate CA rather than
the DTS root CA.
The response will include two properties which you will use later in this guide:
* `id` : The unique identifier for the VICAL Signer. This identifier will be used in subsequent
operations to reference this VICAL Signer.
* `csrPem` : The X.509 Certificate Signing Request (CSR) in PEM format. You will use this CSR to
generate a valid VICAL Signer Certificate (VSC) in the next step.
**Generate and sign the VICAL Signer Certificate (VSC)**
1. Use the **Download** or **Copy** buttons in the **Step 1. Download the VSC Certificate Signing
Request (CSR)** section of the VICAL Signer detail page to obtain the CSR.
2. Using your preferred cryptographic library or tool, generate and sign a VICAL Signer Certificate
(VSC) using the CSR from the previous step. In the 3-tier model, the VSC must be signed by the DTS
intermediate CA's private key. Refer to the
[VSC specific requirements](/docs/digital-trust-service/certificates-overview#signer-certificate-specific-requirements-vsc-and-rsc)
section for details on how to structure a valid VSC.
**Associate the VSC with the VICAL Signer**
Upload the signed VSC to the VICAL Signer and activate it.
1. On the VICAL Signer detail page, under **Step 2. Upload signed VSC**, paste/upload the
PEM-encoded VSC into the **Certificate PEM file** field.
2. Use the *Status* radio button to set the VICAL Signer to **Active**.
3. Select **Update** to associate the VSC and activate the VICAL Signer.
Make a request of the following structure to
[update the VICAL Signer](/docs/digital-trust-service/vical-api-reference/signers#update-a-vical-signer)
to activate and associate it with the generated VSC:
```http filename:"Request"
PUT /v1/ecosystems/certificates/vical-signers/{vicalSignerId}
```
* `vicalSignerId` : Replace with the `id` value obtained when you created the VICAL Signer in the
previous step.
```json filename:"Request body"
{
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICbzCCAhSgAwIBAgIKfS7sskyJEh+DOzAKBggqhkjOPQQDAjAiMSAwCQYDVQQG\r\n...\r\n-----END CERTIFICATE-----\r\n"
}
```
* `active` : This required boolean indicates whether the VICAL Signer is active or not. Can only be
set to `true` when a `certificatePem` is provided. Only active VICAL Signers can be used to sign
VICALs.
* `certificatePem` : This required parameter contains the PEM-encoded VSC created in the previous
step.
**Activate the DTS root CA**
1. Navigate back to the **Certificates** page in the MATTR Portal.
2. Select the DTS root CA you created in the first step.
3. Use the *Status* radio button to set the DTS root CA to **Active**.
4. Select **Update** to activate the DTS root CA.
Make a request of the following structure to
[update the unmanaged DTS root CA](/docs/api-reference/platform/dts-root-ca-certificates/updateDtsCaCertificate)
and activate it:
```http filename:"Request"
PUT /v1/ecosystems/certificates/ca/{dtsCaCertificateId}
```
* `dtsCaCertificateId` : Replace with the `id` value obtained when you registered the unmanaged DTS
root CA.
```json filename:"Request body"
{
"active": true
}
```
### Manually Publish a VICAL [#manually-publish-a-vical]
After you have set up the signing certificate chain, you can publish a VICAL that includes your
participants and their associated credential types. When you publish the VICAL, the MATTR VII
platform will sign a VICAL that includes the information you provided for each participant, along
with the PEM-encoded IACA certificate.
1. Navigate to the **Trust lists** page under the *Digital Trust Service* section.
2. Select the **VICAL (Trusted issuers)** tab.
3. Enter a meaningful *Provider name* to identify the provider of the VICAL. This will be included in the VICAL metadata and used by relying parties to identify the source of the VICAL.
4. Select the **Create** button.
5. Review the preview area where you can see all participants and credential types included in the VICAL.
6. Select **Generate & Publish** when you are ready.\
The VICAL is now generated and published, and a modal is displayed where you can:
* Use the **Download** button to download the VICAL policy file.
* Use the **Copy** button to copy a link to the public endpoint where relying parties can access the policy.
First, make a request of the following structure to
[update the VICAL configuration](/docs/digital-trust-service/vical-api-reference/configuration#update-a-vical-configuration)
and set the VICAL provider name. A VICAL configuration is required before a VICAL can be created:
```http filename:"Request"
PUT /v1/ecosystems/{ecosystemId}/vicals/configuration
```
* `ecosystemId` : Replace with the `id` value of your ecosystem.
```json filename:"Request body"
{
"vicalProvider": "Example Provider"
}
```
* `vicalProvider` : This required parameter is the provider name included in the VICAL metadata and
used by relying parties to identify the source of the VICAL.
Then, make a request of the following structure to
[create (generate and publish) a VICAL](/docs/digital-trust-service/vical-api-reference/general#create-a-vical)
based on your ecosystem policy:
```http filename:"Request"
POST /v1/ecosystems/{ecosystemId}/vicals
```
The response includes the `vicalIssueID` and issuance `date` of the published VICAL. Relying parties
can retrieve it from the public
[Retrieve latest VICAL](/docs/digital-trust-service/vical-api-reference/general#retrieve-latest-vical)
endpoint:
```bash
curl -o vical-latest.cbor \
https://your-tenant.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/vicals/public/latest
```
### Configure VICAL auto-generation and publishing (optional) [#configure-vical-auto-generation-and-publishing-optional]
You can optionally set up auto-generation of your VICAL so it is generated and published on a
schedule.
1. Return to the **Trust lists** page under the *Digital Trust Service* section.
2. Select the **VICAL (Trusted issuers)** tab.
3. Expand the *VICAL configuration* panel.
4. Use the *Generation method* radio button to select **Auto generate**.
5. Use the *Auto generate frequency* dropdown list to select how often you want the VICAL to be automatically generated and published (daily/weekly).
6. Select the **Update** button.
7. Review the preview area where you can see all participants and credential types included in the VICAL.
Note that the VICAL is not generated and published yet. It will only be generated and published automatically based on the frequency you selected in step 5 above. If you want to generate and publish the VICAL immediately, you can select the **Generate & Publish** button.
Make a request of the following structure to
[update the VICAL configuration](/docs/digital-trust-service/vical-api-reference/configuration#update-a-vical-configuration)
and enable scheduled auto-publishing:
```http filename:"Request"
PUT /v1/ecosystems/{ecosystemId}/vicals/configuration
```
```json filename:"Request body"
{
"vicalProvider": "Example Provider",
"autoPublish": {
"enabled": true,
"frequency": "Daily"
}
}
```
* `autoPublish.enabled` : Set to `true` to enable scheduled automatic generation and publishing of
the VICAL.
* `autoPublish.frequency` : Required when `enabled` is `true`. How often the VICAL is automatically
generated and published. One of `Daily` or `Weekly`.
When auto-publishing is enabled, the VICAL is generated and published automatically on the schedule
you set. You can still generate and publish a VICAL immediately at any time by calling the
[Create a VICAL](/docs/digital-trust-service/vical-api-reference/general#create-a-vical) endpoint.
### View Previously Published VICALs (optional) [#view-previously-published-vicals-optional]
1. Return to the **Trust lists** page under the *Digital Trust Service* section.
2. Select the **VICAL (Trusted issuers)** tab.
3. Scroll down and select the **View Previously Published** button to see all previously published VICALs.
4. Use the **Download** button to download the policy file for any previously published VICAL displayed.
Make a request of the following structure to
[retrieve all VICALs](/docs/digital-trust-service/vical-api-reference/general#retrieve-all-vicals)
published in your ecosystem. This endpoint is public and does not require authentication:
```http filename:"Request"
GET /v1/ecosystems/{ecosystemId}/vicals/public
```
The response is a JSON list of the published VICALs with their `vicalIssueID`, issuance `date`, and
`filename`. To download a specific VICAL policy file (a CBOR-encoded file), call the
[Retrieve a VICAL](/docs/digital-trust-service/vical-api-reference/general#retrieve-a-vical)
endpoint with the relevant `vicalIssueId`:
```bash
curl -o vical.cbor \
https://your-tenant.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/vicals/public/{vicalIssueId}
```
## Next steps [#next-steps]
Now that you have published your VICAL, you can share the public endpoint with relying parties so they can consume the VICAL and establish trust in the issuers and credential types included in it. You can also refer to the [VICAL consumption guide](/docs/digital-trust-service/vical-consumption) to learn how relying parties can consume, validate and use a VICAL.
# What is a VICAL and how does it work?
URL: /docs/digital-trust-service/vical-overview
## Introduction [#introduction]
A VICAL (Verified Issuer Certificate Authority List) is a mechanism defined in
[ISO/IEC 18013-5](https://www.iso.org/standard/69084.html) Annex C to support establishing trust in
digital ecosystems where relying parties need to verify mDocs issued by numerous different issuers.
For example, consider the case of relying parties that need to verify Mobile Driver's Licenses
(mDLs). Different states and/or provinces can issue their own mDLs, each signed by a Document Signer
Certificate (DSC) that is itself signed by the state/province unique Issuing Authority Certificate
Authority (IACA).
If you’re someone who needs to verify these mDLs (like a police officer or a business verifying
identities), you would have to individually assess and trust each state/province IACA. This can
become complicated, especially as the number of issuers grows, because you'd have to create and
manage a lot of different trust relationships.
A VICAL solves this by collecting and validating IACAs from different issuing authorities, and then
cryptographically signing them into a single list. Each IACA in the VICAL is associated with:
* An issuing authority that can use this IACA as the root certificate when signing mDocs.
* Credential types for which this IACA can serve as a root certificate.
When a relying party trusts a VICAL they can trust any presented mDoc, given that:
* The credential was issued by an issuing authority that is included in the VICAL.
* The root certificate (IACA) of the chain of certificates used to sign the credential matches the
IACA that is associated with this issuing authority in the VICAL.
* The credential type matches one of the credential types associated with this IACA in the VICAL.
This mechanism enables relying parties to verify mDocs (such as mDLs) from any issuing authority
included in the VICAL without managing multiple separate trust relationships with each issuing
authority. This can greatly simplify the process of verifying mDocs in complex ecosystems, such as
verifying mDLs across various jurisdictions.
A VICAL can be considered a standardized approach to creating a
[Digital Trust Service (DTS)](/docs/digital-trust-service).
## VICAL roles [#vical-roles]
* **VICAL Provider**: Operates the VICAL and provides it as a service to different ecosystem
participants. The VICAL provider collects and validates information from relevant issuers,
compiles it into a standardized VICAL format and distributes it to relying parties.
* **Issuers**: Issue mDocs to holders while attesting the validity of claims included in these
credentials.
* **Relying parties**: Consume the VICAL and use issuers' information to verify presented mDocs.
## VICAL components [#vical-components]
* **VICAL metadata:** This includes general information about the VICAL itself:
* Version.
* Provider.
* Issuance date.
* Unique identifier.
* Next update.
* **VICAL records:** Each record includes the following information that can be used by relying
parties to establish trust in mDocs and the IACAs that are used as their root certificate:
* A trusted IACA that can be used as a root certificate to sign mDocs.
* Issuing authority that can issue mDocs with this IACA.
* Credential types that this IACA can serve as a root certificate for, listed in the `docType`
array. A single VICAL record can authorize an IACA for one or more docTypes.
* Additional IACA information:
* Validity period.
* Unique identifier.
* State/Province.
* Country.
* Public key info (algorithm, curve and value).
* Signature info (algorithm and value).
* Fingerprints.
* Extensions used.
## VICAL structure [#vical-structure]
The decoded VICAL payload follows the CDDL definition below. Each entry in `certificateInfos`
authorizes a single IACA to sign mDocs of one or more `docType` values.
```text
VICAL = {
"version" : tstr, ; VICAL structure version, currently "1.0"
"vicalProvider" : tstr, ; Identifies the VICAL provider
"date" : tdate, ; date-time of VICAL issuance
"vicalIssueID" : uint, ; Uniquely identifies this specific issue of the VICAL
? "nextUpdate" : tdate, ; Optional: date-time of the next planned VICAL update
"certificateInfos" : [+ {
"certificate" : bstr, ; DER-encoded X.509 certificate (IACA)
"serialNumber" : biguint, ; Serial number of the certificate
"ski" : bstr, ; Subject Key Identifier
"docType" : [+ tstr], ; docTypes this IACA is authorized to sign
? "issuingAuthority" : tstr, ; Issuing authority name
? "issuingCountry" : tstr, ; ISO 3166-1 alpha-2 country code
? "stateOrProvinceName" : tstr, ; State or province (sub-national issuers)
? "notBefore" : tdate, ; Certificate validity start
? "notAfter" : tdate ; Certificate validity end
}]
}
```
## How it works [#how-it-works]
1. The VICAL provider establishes its own root certificate with an associated Public Key
Infrastructure (PKI) chain of certificates, based on the [chain of trust](/docs/concepts/chain-of-trust)
model.
2. The VICAL provider collects and validates IACAs from different issuing authorities. Each of these
IACAs are vetted by the VICAL provider and trusted to issue mDocs of specific credential types.
3. The VICAL provider uses their chain of trust end-entity certificate to sign the valid IACAs into
a single list.
4. Each issuing authority uses their own IACA and associated PKI chain of certificates to sign
mDocs.
5. Relying parties can consume the VICAL in one of two ways:
* Download the VICAL directly from the provider’s website.
* Retrieve the VICAL via an endpoint exposed by the provider as an API.
6. When a relying party attempts to verify an mDoc, they validate its signature and referenced PKI
certificate chain against the VICAL to ensure:
* This issuing authority can use this IACA as a root certificate when signing mDocs.
* This credential type can use this IACA as a root certificate.
7. Upon successful validation, the relying party can verify the presented mDoc without having to
create and manage a trust relationship directly with its issuing authority.
Want to see a live VICAL from the inside? Check out the [MATTR Labs VICAL
Viewer](https://tools.mattrlabs.com/vical-viewer) that can render existing
VICAL `.cbor` files into a human-readable format.
## Linked certificates [#linked-certificates]
A **linked certificate** lets a participant rotate the IACA certificate they use in the ecosystem
without breaking trust for relying parties that already trust the previous certificate. When you add
a new certificate for a participant, you can add it as a **successor** to a previously uploaded
certificate by selecting the **predecessor** and uploading a **link certificate** that ties the new
certificate to the previous one.
This preserves trust continuity for the participant across the rotation, so relying parties can
continue to validate the participant's mDocs without first having to consume an updated VICAL.
To add a linked certificate, refer to the [VICAL guide](/docs/digital-trust-service/vical-guide).
# Capabilities
URL: /docs/concepts/capabilities
MATTR [platforms](/docs/concepts/platforms) offer versatile capabilities, crafted from a suite of services
designed to effectively support solutions across any use case:
* **[Credential issuance](/docs/issuance)**: Generation, distribution and lifecycle management of digital verifiable
credentials.
* **[Credential holding](/docs/holding)**: Tools and functionalities that support secure storage,
management, and usage of digital credentials and assets.
* **[Credential verification](/docs/verification)**: Validation of digital credentials against
established criteria and protocols, ensuring their legitimacy and relevance for access or
transactional purposes.
* **[Digital Trust Service](/docs/digital-trust-service)**: Technical orchestration and
management of digital trust frameworks.
* **[Platform management](/docs/platform-management)**: Oversight of MATTR platforms infrastructure and services, ensuring
operational efficiency, security, and scalability.
Some capabilities can be achieved by more than one platform. Choosing the right platform for you
depends on your use case, requirements, resources and implementation timelines. [Contact
us](http://mattr.global/contact) to discuss your options.
# Chain of trust
URL: /docs/concepts/chain-of-trust
Description: Learn how certificates form a chain of trust within MATTR's capabilities, providing assurance needed to validate digital artifacts such as credentials, identities, and verification requests.
## Overview [#overview]
Certificates are used as a foundation of trust within MATTR’s capabilities. They provide the assurance needed to validate digital artifacts—whether that’s an issued credential, an identity, a verification request, or a trusted list.
These certificates are verified based on a chain of trust model, where each certificate is linked to its issuer via a series of certificates:
* **Root certificate**: This digital certificate belongs to the Certificate Authority (CA),
and as its name implies, is at the root of the trust chain. Any certificate along the chain of
trust must be linked to the root certificate.
* **Intermediate certificate**: This digital certificate is linked to the root certificate and acts
as an intermediary between the root certificate and the end-entity, or between other intermediate
certificates. The final certificate in the chain of certificates is referred to as a *leaf
certificate* or an *end-entity certificate*, and is being used to sign the end-entity.
* **End-entity**: This is the final object that is being signed by the leaf/end-entity certificate (the last certificate in the chain
of certificates). For example, when an issuer signs a credential, the end-entity is the credential itself. When a
verifier requests a verification of a credential, the end-entity is the verification request.
For example, this is how the chain of trust model is applied to verify the identity of an issuer signing an mDoc:
The *root certificate* is the **Issuer Authority Certificate Authority (IACA)**. It is used to sign a **Document Signer Certificate (DSC)**, which is the *leaf/end-entity certificate*. The DSC is then used to sign the **Mobile Security Object (MSO)** in the issued mDoc, which is the *end-entity*.
## Key benefits [#key-benefits]
This model offers the following advantages:
* **Scalability**: It allows trust to be extended from a trusted authority down to other entities or
components. For example, a root certificate authority (CA) can delegate trust to intermediate CAs,
which can further delegate trust to end-user certificates. This makes it easier to manage trust
across a large network or ecosystem of trust relationships.
* **Security**: It implements multiple layers of security, with each layer depending on the security
of the previous one. This approach can deter attackers and provide more opportunities to detect
and mitigate breaches. If one component fails or is compromised, the impact can be isolated,
thereby minimizing the impact and preventing further spread of the breach.
* **Transparency and auditability**: Every step in the chain is typically documented and can be
audited, which makes it easier to track where trust was delegated and to ensure that trust
relationships have been correctly established and maintained. This transparency is crucial for
compliance and security audits.
* **Interoperability**: The chain of trust is a well-understood and widely adopted model in various
security standards, such as SSL/TLS for web security, PKI (Public Key Infrastructure) for digital
certificates, and blockchain technologies. This makes it easier to integrate with other systems
and ensures compatibility across different platforms.
Overall, the chain of trust model provides a robust, scalable, and manageable way to establish and
maintain trust across various components of a digital ecosystem, making it a cornerstone of modern
security architectures.
## External certificates [#external-certificates]
MATTR VII offers a robust out-of-the-box solution for certificate management. This includes
generation, storage, and usage of signing certificates, where all the associated private and public
keys managed securely within MATTR’s Key Management System (KMS).
However, some customers prefer (or are required to) manage their own certificates required for
different digital credential workflows. External certificates support in MATTR VII offers flexibility to comply with specific Public
Key Infrastructure (PKI) requirements. It preserves the integrity of the trust chain while
giving customers full control over their cryptographic materials.
The following table depicts the differences between managed and unmanaged certificates in MATTR VII:
| Internal (Managed) Certificates | External (Unmanaged) Certificates |
| :--------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------- |
| MATTR VII provisions and manages all required certificates | MATTR VII provides tools to assist customers in managing their own certificates |
| All private keys are managed by MATTR VII | Customers manage the root CA private key, while MATTR VII manages intermediate and end-entity certificates keys |
| MATTR VII automatically generates and signs certificates required for signing operations | MATTR VII integrates external certificates provided by the customer into signing workflows |
| Certificates are automatically validated and managed by MATTR VII | Customers must ensure their certificates meet MATTR’s validation requirements |
| MATTR VII handles all certificate lifecycle management tasks | Customers must manage the lifecycle of their own certificates |
In both models, in every signing operation MATTR VII must be able to associate and understand:
* Who is performing the signing: This is an
internal MATTR entity that represents a signer. It contains the signer’s unique identifier and (if
managed) the private key in KMS.
* What certificate is associated with that signer: This is a X.509 certificate that links the
signer’s public key to its identity. In the external (unmanaged) certificate flow, the customer is
responsible for creating and uploading this certificate to MATTR VII, so it can be associated with
the corresponding signer entities.
* What root certificate authority (CA) issued the certificate: This is the root CA that issued the
certificate associated with the signer. In the external (unmanaged) certificate flow, the
customer is responsible for registering this root CA in MATTR VII, so it can be used to validate
the certificate chain of trust.
The **signer** is like a digital "signing machine" within MATTR, whereas the **signer certificate** is the official "license" issued by a trusted authority stating the signer
is valid and authorized.
### Enabling external certificates [#enabling-external-certificates]
The following steps outline the process of enabling and using external certificates in MATTR VII:
1. **Root CA certificate generation**: The customer generates a private/public key pair and uses the
private key to sign a self-signed root certificate.
2. **Unmanaged root CA certificate registration**: The customer uses a MATTR VII endpoint to register the unmanaged
root CA certificate by providing it in PEM format, which includes the public key and other necessary
information. This root CA certificate is not managed by MATTR VII, meaning MATTR will not store the private key
or handle its lifecycle. A root CA certificate identifier is generated by MATTR VII and is used to reference
this unmanaged root CA certificate in subsequent operations.
3. **Signer creation**: The customer uses a MATTR VII endpoint to create a
signer which references the unmanaged root CA certificate identifier.
4. **Certificate Signing Request (CSR)**: When a signer is created, MATTR VII
generates a new private/public key pair:
* The *private* key is stored in the MATTR KMS and used to sign a CSR.
* The *public* key is included in the CSR, alongside all necessary information to enable the
customer to issue a valid certificate that meets MATTR’s requirements.
5. **Signer certificate generation**: The customer uses the CSR to generate and sign a signer certificate which meets the
following requirements:
* Must include the public key from the CSR.
* Must be signed by the root CA certificate private key.
* Must match the structure and values expected by MATTR, as described in the CSR.
6. **Certificate upload**: After the signer certificate is signed, the customer uses a MATTR VII endpoint to
upload it in PEM format and associate it with the signer.
7. **Certificate validation**: MATTR VII will validate the uploaded certificate to ensure it is
signed by the expected root CA certificate, contains the correct public key, and includes the necessary X.509
extensions and usages.
8. **Signer and root CA certificate activation**: Once the certificate is validated, the signer, followed by the registered unmanaged root CA certificate, can be activated, allowing them to be used
in signing operations. MATTR VII will not select any signers if their referenced root CA certificate is not active.
9. **Certificate lifecycle management**: The customer is responsible for managing the lifecycle of
the external certificates, including renewal and revocation. MATTR VII will not automatically
handle these tasks for unmanaged root CA certificates.
## Certificates rotation [#certificates-rotation]
To support certificates rotation, a single MATTR VII tenant can manage multiple root CA
certificates. This approach allows issuers to distribute new root CAs in advance, ensuring
seamless continuity of relying party reliance over their trust frameworks.
The process involves creating a new root CA certificate without activating it, which means it
will not be used by MATTR VII in any sign operations. However, this inactive root CA can be shared
with relying parties ahead of its activation. When ready, the customer can activate the new root
CA which automatically deactivates the old one. After this switch, new signer certificates will be signed using the new root
CA, and relying parties will be able to verify end-entities signed by these signer certificates immediately, as the
root CA was pre-distributed to them.
Root CA rotation should be performed as follows:
1. Identify that the current active root CA (**root CA #1**) must be rotated as it is about
to expire.
2. Create a new **root CA #2**.
3. Distribute **root CA #2** to all relying parties.
Step #3 must be performed out-of-band. Creating a new root CA on MATTR VII doesn't update
any existing trusted lists which include this root CA.
4. Set **root CA #2** to `active`.
## Frequently asked questions [#frequently-asked-questions]
### What is a chain of trust? [#what-is-a-chain-of-trust]
A chain of trust is a sequence of digital certificates that link an end-entity, such as a signed
credential, back to a trusted root certificate authority. Each certificate in the chain is
cryptographically signed by the one above it, allowing a verifier to confirm that the end-entity
was signed by an authority that ultimately traces back to a recognised trust anchor.
### What is the role of the root certificate? [#what-is-the-role-of-the-root-certificate]
The root certificate belongs to a Certificate Authority (CA) and sits at the top of the trust
chain. It is self-signed and is the anchor that every other certificate in the chain must link back
to. Relying parties are configured to trust specific root certificates, and that trust is what
makes the rest of the chain meaningful.
### What is the difference between an internal and an external certificate in MATTR VII? [#what-is-the-difference-between-an-internal-and-an-external-certificate-in-mattr-vii]
Internal (managed) certificates are generated and managed entirely by MATTR VII, including all
private keys held in MATTR's Key Management System. External (unmanaged) certificates are managed
by the customer, who controls the root CA private key while MATTR VII manages intermediate and
end-entity certificate keys. External certificates are useful when customers have specific Public
Key Infrastructure requirements.
### Why does a chain of trust matter for verifiable credentials? [#why-does-a-chain-of-trust-matter-for-verifiable-credentials]
It lets a verifier confirm that a credential was signed by an authority that is recognised under
the relevant trust framework without contacting the issuer at the time of verification. The
cryptographic chain provides offline, deterministic proof of issuer authenticity.
### How are root CAs rotated without breaking verification? [#how-are-root-cas-rotated-without-breaking-verification]
A new root CA is created and distributed to relying parties before it is activated. Once activated,
the previous root CA is automatically deactivated and new signer certificates are issued under the
new root. Because relying parties already have the new root, verification continues without
interruption.
# What is a verifiable credential ecosystem?
URL: /docs/concepts/credential-ecosystem
Description: Learn how a verifiable credential ecosystem works, including the roles of issuer, holder, verifier, and trust framework, and how they interact to enable secure digital credential exchange.
This page explains how a **verifiable digital credential ecosystem** works.
If you are new to digital credentials, read [Verifiable credentials](/docs/concepts/verifiable-credentials) first. That page explains what a verifiable credential is and why cryptographic verification matters. This page builds on that foundation by focusing on the **ecosystem roles**, the **trust layer**, and the **interaction flow** between participants.
## What is a verifiable credential ecosystem? [#what-is-a-verifiable-credential-ecosystem]
A **verifiable credential ecosystem** is the set of participants, rules, and technical components that allow digital credentials to be:
* issued by a trusted organisation
* held by an individual or organisation
* presented when proof is needed
* verified by another party
* trusted across systems, organisations, and jurisdictions
At a minimum, most ecosystems include four core roles:
* **Issuer**: creates and signs the credential
* **Holder**: receives, stores, and presents the credential
* **Verifier**: requests and checks the credential
* **Trust framework** or **trust registry**: provides the governance and trust signals that help participants decide which issuers, wallets, keys, and rules to trust
These roles work together to move trust from fragmented, manual checks into a reusable digital model.
## Why this ecosystem model matters [#why-this-ecosystem-model-matters]
A verifiable credential is not useful on its own. Its value comes from being part of an ecosystem where multiple parties can rely on it consistently.
That ecosystem model matters because it helps participants answer four essential questions:
1. **Who issued this credential?**
2. **Can I trust that issuer?**
3. **Has the credential been changed, expired, or revoked?**
4. **Is the holder sharing the right information for this transaction?**
A well-designed ecosystem makes those questions easier to answer in a secure, privacy-aware, and scalable way.
## The four core roles in a verifiable credential ecosystem [#the-four-core-roles-in-a-verifiable-credential-ecosystem]
### Issuer [#issuer]
An **issuer** is the organisation that creates and signs a credential.
The issuer is the source of the claims inside the credential. For example, an issuer might be:
* a government issuing a mobile driver’s licence
* a university issuing a degree credential
* a bank issuing a proof-of-account or customer credential
* an employer issuing an employee ID credential
The issuer is responsible for:
* confirming the underlying data is accurate
* defining what the credential contains
* signing the credential so others can verify its authenticity
* managing status over time, such as expiry, suspension, or revocation
* operating within the rules of the relevant trust framework
#### What value does the issuer gain? [#what-value-does-the-issuer-gain]
Issuers benefit because they can provide credentials that are:
* **portable**, so the holder can reuse them across services
* **tamper-evident**, because the credential is cryptographically signed
* **easier to verify**, reducing follow-up checks and manual validation
* **more interoperable**, when aligned to shared standards and trust rules
For issuers, this can reduce operational overhead, improve service delivery, and extend the usefulness of authoritative data beyond a single channel or portal.
### Holder [#holder]
A **holder** is the person or organisation that receives and controls the credential.
The holder typically stores credentials in a wallet application and chooses when to present them. The holder is not the source of the credential’s authority. Instead, the holder is the party that has been given the credential and can use it to prove something about themselves.
Examples include:
* a citizen holding a mobile driver’s licence
* a student holding a degree credential
* an employee holding a digital staff ID
* a business holding a licence or accreditation credential
The holder is responsible for:
* receiving and storing the credential
* keeping access to it secure
* reviewing presentation requests
* deciding when to share it
* consenting to the disclosure of requested information
#### What value does the holder gain? [#what-value-does-the-holder-gain]
Holders benefit because verifiable credentials can give them:
* **more control** over when and where their information is shared
* **more convenience** across repeated interactions
* **better privacy**, especially when selective disclosure is supported
* **less repetition**, because the same credential can be reused across services
* **greater transparency**, because they can see what is being requested
For holders, the ecosystem works best when trust does not require oversharing.
### Verifier [#verifier]
A **verifier** is the organisation that requests and checks a credential.
The verifier relies on the credential to make a decision, such as whether to:
* allow access to a service
* approve an application
* confirm age or identity
* validate a qualification or licence
* onboard a customer or employee
The verifier is responsible for:
* requesting the right information for the interaction
* validating the credential’s authenticity and integrity
* checking whether the issuer is trusted under the relevant framework
* checking status information such as expiry or revocation
* applying business or policy rules to the verified result
#### What value does the verifier gain? [#what-value-does-the-verifier-gain]
Verifiers benefit because verifiable credentials can provide:
* **stronger assurance**, through cryptographic verification
* **faster decisions**, with less manual review
* **reduced fraud risk**, because tampering is detectable
* **better privacy alignment**, by collecting only what is needed
* **less integration complexity**, when ecosystems use shared standards and trust rules
For verifiers, the key value is not just seeing data, but being able to trust where it came from and whether it is still valid.
### Trust framework or trust registry [#trust-framework-or-trust-registry]
A verifiable credential ecosystem also needs a trust layer.
This is often described as a **trust framework**, and in some implementations it is supported by a **trust registry**.
A **trust framework** is the set of legal, policy, operational, and technical rules that define how the ecosystem works. It answers questions such as:
* who is allowed to issue credentials
* what standards or schemas must be followed
* what assurance requirements apply
* how participants are onboarded and governed
* how keys, identifiers, and trust anchors are managed
* how disputes, compliance, and liability are handled
A **trust registry** is a technical mechanism that can publish or make available trust information used by ecosystem participants. Depending on the ecosystem, it may contain information about:
* trusted issuers
* trusted verifier organisations
* approved wallet providers
* public keys or trust chains
* supported schemas or credential types
* accreditation or governance status
#### Why distinguish trust framework from trust registry? [#why-distinguish-trust-framework-from-trust-registry]
These terms are related, but they are not the same.
* A **trust framework** is the broader governance model.
* A **trust registry** is one technical way to expose trust information inside that model.
Some ecosystems may use a formal registry. Others may rely on federation metadata, certificate chains, government trust lists, or another trust distribution mechanism.
#### What value does the trust layer provide? [#what-value-does-the-trust-layer-provide]
The trust layer benefits everyone:
* **Issuers** gain a recognised path to ecosystem acceptance
* **Holders** gain confidence that their credentials will be widely usable
* **Verifiers** gain a reliable way to determine who and what to trust
* **Ecosystem operators** gain a scalable way to govern participation and assurance
Without a trust layer, a verifier may be able to confirm that a credential was signed, but not whether the signer should be trusted for that credential type.
## Credential lifecycle [#credential-lifecycle]
Credential lifecycle refers to the stages a credential goes through from creation to eventual expiry or revocation. Understanding this lifecycle is key to building secure and privacy-preserving digital credential solutions.
At a high level, the interaction flow is simple:
1. The issuer creates and signs a credential
2. The holder receives and stores it
3. The verifier requests proof from the holder
4. The holder presents the credential or a derived proof
5. The verifier checks the credential, the issuer trust chain, and status information
6. The verifier makes a decision
The trust framework sits around this exchange and defines the rules that make the result meaningful.
Beyond issuance and presentation, credential lifecycle events may include updates, revocation or expiry. The ecosystem must support these lifecycle events to maintain trust over time:
* Credential update: The issuer may need to update a credential, such as adding new claims or correcting information. The ecosystem should support a secure way to issue an updated credential and manage the lifecycle of the old one.
* Credential revocation: If a credential is compromised, no longer valid, or the holder’s relationship with the issuer changes, the issuer may need to revoke it. The ecosystem should support a way for verifiers to check revocation status during verification.
* Credential expiry: Some credentials are only valid for a certain period. The ecosystem should support expiry information and ensure verifiers can check it.
### Ecosystem flow at a glance [#ecosystem-flow-at-a-glance]
### What happens during issuance? [#what-happens-during-issuance]
Issuance is the process where an issuer creates a credential for a specific holder.
During issuance, the issuer typically:
* confirms the subject’s eligibility
* prepares the credential data
* signs the credential using its cryptographic keys
* delivers the credential to the holder’s wallet
* records or publishes any needed status information
The result is a credential that the holder can later present.
#### Issuance flow [#issuance-flow]
### What happens during presentation and verification? [#what-happens-during-presentation-and-verification]
Presentation is the process where the holder shares proof with a verifier.
Verification is the process where the verifier checks that proof and decides whether it can rely on it.
During this interaction, the verifier may check:
* whether the credential is authentic
* whether the issuer is trusted
* whether the credential is expired or revoked
* whether the proof matches the requested data
* whether holder binding or other policy requirements are satisfied
The holder may present:
* the full credential
* selected attributes
* a derived proof, depending on the credential format and ecosystem design
#### Verification flow [#verification-flow]
## What each role needs from the others [#what-each-role-needs-from-the-others]
Thinking in architecture terms, each role depends on the others in different ways.
### What the issuer needs [#what-the-issuer-needs]
The issuer needs:
* a way to identify the holder
* a credential format and schema
* signing keys and trust anchor relationships
* a delivery channel to the holder
* a status management approach
* governance rules that define when it is authorised to issue
### What the holder needs [#what-the-holder-needs]
The holder needs:
* a wallet or holder application
* a secure way to receive credentials
* a way to review requests from verifiers
* a way to present credentials or proofs
* confidence that the ecosystem protects privacy and usability
### What the verifier needs [#what-the-verifier-needs]
The verifier needs:
* a way to request the right proof
* a way to validate signatures and credential structure
* access to trust information
* access to status information when required
* policy rules that define what is acceptable for the use case
### What the trust layer needs [#what-the-trust-layer-needs]
The trust layer needs:
* governance and operating rules
* participant onboarding and accreditation processes
* trust anchor management
* mechanisms to publish or distribute trust information
* change management across standards, participants, and assurance requirements
## What makes an ecosystem trustworthy? [#what-makes-an-ecosystem-trustworthy]
A verifiable credential ecosystem is not trustworthy just because it uses cryptography.
It becomes trustworthy when technical verification is combined with governance.
That usually includes:
* cryptographic integrity, so credentials cannot be altered undetectably
* issuer authenticity, so verifiers know who signed the credential
* status management, so expired or revoked credentials can be identified
* trust anchors and frameworks, so verifiers know which issuers are accepted
* privacy-preserving presentation, so only necessary data is shared
* clear ecosystem rules, so participants understand obligations and assurance levels
This is why trust frameworks matter so much. Cryptography can prove that a credential was signed. Governance determines whether that signature should be relied on in a real-world context.
## Centralised verification versus ecosystem trust [#centralised-verification-versus-ecosystem-trust]
Traditional verification often depends on a central database lookup each time proof is needed.
A verifiable credential ecosystem works differently. The proof travels with the credential, and the verifier can often validate it independently using cryptographic proofs, trust metadata, and status information.
This changes the architecture in important ways:
* trust becomes more portable
* verification can be more scalable
* privacy can improve because fewer central lookups are required
* interoperability becomes more achievable across organisations
The result is not the removal of trust, but the distribution of trust into a framework that can be checked and reused more consistently.
## Example: how the ecosystem works for a mobile driver’s licence [#example-how-the-ecosystem-works-for-a-mobile-drivers-licence]
A mobile driver’s licence ecosystem might work like this:
* A transport authority acts as the issuer
* A citizen acts as the holder
* A police officer, retailer, or service provider acts as the verifier
* A government trust framework defines which issuers, keys, wallets, and policies are accepted
In practice:
* The transport authority issues an mDL to the citizen
* The citizen stores it in a wallet
* A verifier requests proof of identity, age, or driving entitlement
* The citizen presents the required information
* The verifier checks the proof, issuer trust, and status
* The verifier accepts or rejects the interaction based on policy
That same model can apply to other credential types, including education, health, workforce, and financial use cases.
## Frequently asked questions [#frequently-asked-questions]
### What is a verifiable credential ecosystem? [#what-is-a-verifiable-credential-ecosystem-1]
A verifiable credential ecosystem is the set of participants, rules, and technical components that
allow digital credentials to be issued by a trusted organisation, held by an individual or
organisation, presented when proof is needed, verified by another party, and trusted across
systems, organisations, and jurisdictions. Most ecosystems include four core roles, issuer, holder,
verifier, and a trust framework or trust registry.
### What is the difference between an issuer and a verifier? [#what-is-the-difference-between-an-issuer-and-a-verifier]
An issuer creates and signs a credential. A verifier requests and checks it. The issuer is the
source of the claims inside the credential. The verifier relies on the credential to make a
decision, such as whether to grant access or approve an application.
### Is the holder always a person? [#is-the-holder-always-a-person]
No. In many ecosystems the holder is a person, but it can also be an organisation, device, or software agent, depending on the use case and trust model.
### What is the difference between a trust framework and a trust registry? [#what-is-the-difference-between-a-trust-framework-and-a-trust-registry]
A trust framework is the broader governance model for the ecosystem. It defines the legal, policy,
operational, and technical rules. A trust registry is one technical mechanism that can publish
trust information used within that framework, such as the list of accredited issuers or trusted
keys.
### Can a verifier trust any digitally signed credential? [#can-a-verifier-trust-any-digitally-signed-credential]
No. A verifier must usually confirm both that the credential is cryptographically valid and that
the issuer is trusted for that credential type under the relevant framework. A valid signature
alone does not establish that the signer is the right authority to issue the credential.
### Does the issuer need to be online every time a credential is verified? [#does-the-issuer-need-to-be-online-every-time-a-credential-is-verified]
Not always. In many verifiable credential models, the verifier can validate the credential without contacting the issuer directly for every transaction, although status checks or trust resolution may still be required depending on the design.
### Why is the trust layer necessary? [#why-is-the-trust-layer-necessary]
Without a trust layer, a verifier may know that a credential was signed, but not whether the signer
is recognised, authorised, or governed appropriately for the use case. The trust framework and
trust registry provide the governance signals that make ecosystem-wide verification meaningful.
## Summary [#summary]
A verifiable digital credential ecosystem is made up of issuers, holders, verifiers, and a trust layer that governs how they interact.
* Issuers create and sign trusted credentials
* Holders receive, store, and present them
* Verifiers request and validate proofs
* Trust frameworks and trust registries provide the governance and trust signals that make ecosystem-wide verification possible
Together, these roles allow trust to move with the credential, rather than staying trapped in isolated databases and manual checks.
# What is a decentralized trust model?
URL: /docs/concepts/decentralized-trust-model
Description: Learn how decentralized digital trust differs from earlier identity models, the technology that enables it, and the mind shifts required for policy makers and stakeholders.
Digital trust is moving from a federated model, in which intermediaries authenticate users and assert their identity to relying parties, to a decentralized one, in which the holder receives verifiable credentials and presents them directly. This shift is not a technology upgrade. It is a change in the architecture of trust itself.
Several jurisdictions are operationalizing decentralized digital identity programs, including the European Union (eIDAS 2.0 and the European Digital Identity Wallet), Australia, New Zealand, the United Kingdom, Canada, and Singapore. The standards stack is mature, the architectural model is widely understood, and credential schemes are entering production. The questions that remain are governance questions, and they require policy makers, regulators, and ecosystem stakeholders to reframe assumptions formed in earlier eras.
This page explains what is genuinely different about the decentralized trust model, the technology that enables it, and the shifts in thinking required to govern it well.
## The three architectural eras of digital identity [#the-three-architectural-eras-of-digital-identity]
### Era one: Paper and the trusted holder [#era-one-paper-and-the-trusted-holder]
For most of the twentieth century, identity verification meant presenting a physical document. Governments issued passports, birth certificates, and driver licenses. The citizen carried them. A relying party (a bank, an employer, or a border officer) inspected the document, made a judgment about its authenticity, and recorded what they needed.
The safeguards in this era were operational: document security features, controlled issuance, in-person inspection. What happened at the point of presentation was largely outside the issuer's view.
This era's privacy properties were accidental rather than designed. There was no national identity database because there was no practical way to build one. Each relying party kept its own records, and linkage between them was difficult.
### Era two: Federated identity and the trusted intermediary [#era-two-federated-identity-and-the-trusted-intermediary]
From the early 2000s, governments and large institutions moved identity verification online. The architecture that emerged was **federated**. A single trusted intermediary, the identity provider, authenticated the user and then asserted to relying parties that the user was who they claimed to be. National schemes followed this model, alongside consumer-grade equivalents such as Login with Google and Login with Apple.
The federated model solved a real problem. It removed the need for every relying party to verify identity independently. But it introduced a new one: **the identity provider sees every transaction.** Each verification request flows through the intermediary, which can build a complete view of which services a person accesses.
The safeguards in this era were contractual and regulatory. Identity providers were required to handle data carefully, retain it only as long as necessary, and submit to oversight. Privacy law and binding terms did the work. But the architecture itself was surveillance-capable by design: the intermediary had to see the transaction in order to authenticate it.
### Era three: Decentralized credentials and the trusted holder, returned [#era-three-decentralized-credentials-and-the-trusted-holder-returned]
The decentralized model returns the holder to the center, but with cryptographic guarantees the paper era could not offer. An authoritative issuer issues a [verifiable credential](/docs/concepts/verifiable-credentials) to the holder's wallet. The holder presents that credential to relying parties directly, without an intermediary in the transaction loop. The credential is cryptographically signed by the issuer, so the relying party can verify its authenticity without contacting the issuer. The issuer cannot see where the credential is being used.
What is genuinely different about era three is not the cryptography itself. It is the consequence: **no party in the system holds a complete record of how a person uses their identity.** Not the issuer. Not a federation provider. Not a single trust registry. The data minimization that was accidental in era one and contractual in era two is now **architectural** in era three.
## The technology that enables decentralized trust [#the-technology-that-enables-decentralized-trust]
The decentralized trust model rests on a stack of open, internationally maintained standards that have matured significantly over the last five years.
| Layer | Standard | Role |
| -------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------ |
| Credential data model | W3C Verifiable Credentials Data Model 2.0 | Defines the structure and semantics of a verifiable credential |
| mobile Driver’s License (mDL) | ISO/IEC 18013-5 | Specifies the data model and presentation protocol for mobile driving licenses (mDL) |
| Selective disclosure credentials | SD-JWT VC | A credential format that supports presenting only selected attributes |
| Presentation protocol | OpenID for Verifiable Presentations (OID4VP) | Defines how a relying party requests and receives credentials from a wallet |
| Issuance protocol | OpenID for Verifiable Credential Issuance (OID4VCI) | Defines how an issuer delivers credentials to a wallet |
| Browser integration | W3C Digital Credentials API | Allows web pages to request credentials from a wallet through the browser |
Together, these standards make it possible for an issuer to mint a credential once, for a holder to store and present it from any compliant wallet, and for a verifier to validate it independently without contacting the issuer or any central intermediary.
Two properties of this stack matter particularly for governance:
* **Cryptographic signatures** make credentials tamper-evident and bind them to the issuer without requiring a live lookup at verification time. The verifier can confirm authenticity offline.
* **Selective disclosure** lets the holder present only the specific facts a verifier needs (for example, "over 18" rather than a full date of birth). This is how the architecture enforces data minimization in practice. See [Selective disclosure](/docs/concepts/selective-disclosure) for more.
Trust is anchored in cryptographic keys and governed by explicit trust frameworks rather than inferred from reputation or jurisdiction. See [Chain of trust](/docs/concepts/chain-of-trust) for how issuer keys are anchored to recognized trust roots.
## Four layers of a credential ecosystem [#four-layers-of-a-credential-ecosystem]
It is helpful to read a credential ecosystem in terms of four functional layers, because the policy
questions tend to sit cleanly within one or another of them. This framing is not specific to any
single jurisdiction. It is a useful way to read what is actually happening in deployed programs.
The EU's Architecture and Reference Framework, the ISO 18013 family of standards, and most national
trust frameworks make similar distinctions, sometimes with different vocabulary.
| Layer | What it does | Where it sits |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Issuing authority** | Decides what may be asserted, at what level of assurance, and under what conditions. Holds the revocation mandate. | The agency with the legal standing and the authoritative source data to create credentials. |
| **Issuance platform** | Turns the issuing authority's instructions into cryptographically signed credentials and delivers them to the holder's wallet. | The technical infrastructure. It has no independent credential authority. It does what the issuing authority tells it to do, to a standards-compliant specification. |
| **Trust framework** | Defines who counts as accredited, what technical standards apply, what privacy and security obligations attach to accredited parties, and how enforcement works. | The governance layer. Typically established by primary legislation, refined through regulations and standards, and operated by an accreditation authority. |
| **Facilitation mechanism** | Holds credentials and presents them. Implements selective disclosure, refuses to "phone home" to its supplier during presentations, supports biometric access on the device, and supports user-initiated revocation. | The citizen-facing software. The wallet. It operates under the citizen's control rather than the issuing authority's. |
Outside all four layers sit **relying parties**. These are the businesses, agencies, and
organizations that receive credential presentations. They are bound by general data protection law,
such as the GDPR in Europe and the Privacy Act in New Zealand and Australia, but they are not, in
most jurisdictions, bound by the trust framework itself unless they are also operating an
accredited service. Closing this gap is one of the most consistent open policy questions across
credential programs. See [Policy considerations](/docs/concepts/policy-considerations#trust-framework-scope-and-the-relying-party-gap)
for the design choices it opens up.
This four-layer view lets you point precisely at where each policy question lives, rather than
collapsing them all into a single "decentralized identity" conversation. Most of the policy work
sits at the issuing authority and trust framework layers. The issuance platform and facilitation
mechanism are largely technical, operating to standards set by the layers above them.
The assurance that flows through all four layers is established before a credential is issued,
through identity proofing and holder binding. Because the issuer is out of the transaction loop once
the credential is in the wallet, the strength of that upfront step sets the ceiling for everything
downstream. See [Identity proofing and holder binding](/docs/concepts/identity-proofing-and-holder-binding).
## Why architectural shifts matter for governance [#why-architectural-shifts-matter-for-governance]
Each era's safeguards were designed for that era's architecture. When governance instruments from earlier eras are carried forward unchanged, two failure modes follow.
The first is **over-collection by reflex**: treating a decentralized system as if it were federated and requiring providers to log, retain, or report on individual transactions. This recreates the surveillance properties the architecture was specifically designed to eliminate.
The second is **under-protection by assumption**: treating decentralization as if it solved every privacy and integrity question on its own. It does not. The new architecture moves the locus of risk away from the identity provider's database and onto the relying party's intake form. If the relying party asks for more than it needs, the holder presents more than necessary, and the data ends up retained in places the architecture cannot reach.
The work of policy in era three is to move the safeguards to where the risks actually sit.
## Six shifts in governance focus [#six-shifts-in-governance-focus]
### From gatekeeping access to setting issuance terms [#1-from-gatekeeping-access-to-setting-issuance-terms]
In the federated era, the identity provider controlled access transaction by transaction. Every verification request was authorized in real time. Governance worked by sitting in the loop.
In the decentralized era, **the issuer is out of the loop by design.** The credential travels independently of the issuer. Governance must move upstream, to the moment of issuance, and downstream, to the conditions under which relying parties may consume credentials. The lever is no longer access control. It is **issuance policy and accreditation**.
In practice, this means specifying at issuance time what the issuer expects of any party that subsequently consumes the credential, and ensuring those expectations are technically and legally enforceable.
### From surveillance-based oversight to architectural safeguards [#2-from-surveillance-based-oversight-to-architectural-safeguards]
Federated systems supported oversight through logs. The intermediary saw every transaction and could be required to report on patterns, anomalies, and breaches. Oversight was retrospective and pattern-based.
Decentralized systems cannot support that model without breaking their core property. Many trust frameworks now explicitly prohibit issuers, wallets, and trust framework providers from tracking or correlating verification activity. Policy makers seeking equivalent oversight assurance must look to architectural and statistical instruments instead of transactional ones:
* **Aggregate issuance metrics** (credentials issued, revocation rates, expiry patterns) give regulators system-level visibility without individual tracking.
* **Mandatory incident reporting** from accredited issuers, wallets, and verifiers provides accountability without surveillance.
* **Operationally immediate revocation** gives the issuer prospective protective capability that is more powerful than retrospective logging, because it stops harm in flight rather than describing it after the fact.
### From "what data do we hold" to "what claims can be presented" [#3-from-what-data-do-we-hold-to-what-claims-can-be-presented]
The federated era was preoccupied with the security of held data: breach response, encryption at rest, access controls. These remain important, but they answer a question that matters less in era three.
The decentralized era's central question is **what claims can be presented and verified without underlying data ever transferring.** "Over 18: yes" is a claim. "Citizen: yes" is a claim. Neither requires a date of birth or a passport number to leave the wallet. Selective disclosure and zero-knowledge proofs are the technical instruments. Policy frames whether they are mandated, encouraged, or optional.
A reasonable default is that for any high-value credential issued by a government or regulated body, selective disclosure should be the default presentation mode, and full-record disclosure should require a specific justification by the relying party.
### From purpose limitation as compliance to purpose binding as architecture [#4-from-purpose-limitation-as-compliance-to-purpose-binding-as-architecture]
Purpose limitation has long been a principle of privacy law: information collected for one purpose should not be used for another. Historically this has been auditable but not architecturally enforced.
In era three, purpose binding can be embedded in the credential itself. An issuer can specify that a particular attribute is presentable only for age verification, or only to accredited services, or only for a specific declared purpose. OID4VP requires the relying party to declare its purpose machine-readably before the credential is presented. The wallet enforces the issuer's policy. The user sees the purpose.
The architecture does the work that compliance auditing previously did after the fact.
### From relying-party trust by reputation to relying-party trust by accreditation [#5-from-relying-party-trust-by-reputation-to-relying-party-trust-by-accreditation]
In the federated era, the identity provider implicitly extended trust to whichever relying parties it chose to serve. The relying party's reputation, contractual position, or sector regulation did the work of justifying that trust.
In era three, **accreditation under a trust framework** is the formal instrument. For credentials carrying high-assurance attributes (nationality, citizenship, regulated qualifications), consumption can be restricted to accredited services. Lower-assurance attributes can be accessible to parties that have signed binding issuer terms without full accreditation.
Trust frameworks and trust registries are the enforcement mechanism. They let the ecosystem know which issuers, wallets, and verifiers meet defined obligations without putting the issuer in the transaction loop. See [Verifiable credential ecosystem](/docs/concepts/credential-ecosystem) for more on these roles.
### From data linkage as capability to data linkage as risk [#6-from-data-linkage-as-capability-to-data-linkage-as-risk]
Federated systems often combined data across agencies or institutions to enrich identity assertions. The capability was valued; the risk was managed contractually.
In era three, cross-organization data combination becomes the principal residual risk inside large institutional ecosystems, even as the risks outside them recede. Combining citizenship data with driver licensing data with business registration data to construct profiles no single dataset was collected to produce is the surveillance-by-linkage scenario decentralized architectures were specifically designed to prevent, and which institutions should not replicate internally.
A useful discipline is **verification without retention**: where an issuer needs to confirm a fact against another authoritative source, the claim should be verified at issuance time and the source data discarded. Verification happens, the credential is issued, the third-party data is not retained.
## Common pitfalls when adopting a decentralized model [#common-pitfalls-when-adopting-a-decentralized-model]
Practitioners new to the model often carry forward instincts that no longer serve. Among the most common:
* **Logging all verification events centrally**, because that is how oversight worked before. This breaks the architecture's privacy guarantees and is often prohibited by the trust framework rules that govern it.
* **Requiring every credential to be checked against a live issuer endpoint at presentation time.** This re-creates the federated dependency the model was designed to eliminate. Use offline-verifiable signatures and published status lists instead.
* **Asking for full credentials when only a single attribute is needed.** This pushes the data-minimization principle outside the architecture's enforcement zone. Use selective disclosure by default.
* **Treating the wallet as a passive container.** In era three, the wallet enforces issuer policy, mediates user consent, and represents the holder. It is a governance-bearing component, not just a user interface.
* **Assuming the technology alone delivers privacy.** It does not. The architecture removes specific risks (issuer-side surveillance, central correlation) but does not address what relying parties do with the data once they receive it. That work happens in issuance policy, accreditation, and trust framework rules.
## What this means for policy makers and stakeholders [#what-this-means-for-policy-makers-and-stakeholders]
Decentralized trust does not reduce the need for governance. It changes where governance is applied and what instruments are available.
The strongest protections in a decentralized ecosystem come from designing the system so that misuse is **technically constrained**, not from adding compliance obligations on top of an architecture that already permits the harm. Where policy work is grounded in the architecture, the resulting safeguards are durable, technically enforceable, and proportionate. Where it is not, safeguards either become compliance overhead that the architecture has already made redundant, or they reintroduce surveillance properties the architecture was specifically designed to remove.
For policy makers, governance bodies, and ecosystem stakeholders, the recommendation is to **use the architecture**. Decide what claims may be issued, by whom, to whom, and under what conditions. Specify selective disclosure as the default. Treat trust frameworks and accreditation as primary instruments. Move oversight from transactional logging to aggregate metrics and incident reporting. Recognize that the harder work is not the technology. It is building the institutional capability to operate the ecosystem well: revocation operations, accreditation compliance, relying-party oversight, fraud response, and equity and access management.
The technology is ready. The standards are in place. The remaining work is governance, and it is the right hard work to do.
## Frequently asked questions [#frequently-asked-questions]
### What is a decentralized trust model? [#what-is-a-decentralized-trust-model]
A decentralized trust model is an approach to digital identity in which an authoritative issuer
issues a verifiable credential to a holder's wallet, and the holder presents that credential
directly to relying parties without an intermediary in the transaction loop. The credential is
cryptographically signed by the issuer so the relying party can verify it independently, and no
central party sees where the credential is being used.
### How is decentralized trust different from federated identity? [#how-is-decentralized-trust-different-from-federated-identity]
In a federated model, an identity provider authenticates the user and asserts their identity to
relying parties, so the intermediary sees every transaction. In a decentralized model, the issuer
is out of the loop once the credential is issued. The relying party verifies the credential
cryptographically without contacting the issuer, and no party holds a complete record of how a
person uses their identity.
### What technical standards does decentralized trust rely on? [#what-technical-standards-does-decentralized-trust-rely-on]
The decentralized trust model rests on a stack of open international standards. These include the
W3C Verifiable Credentials Data Model 2.0 for credential structure, ISO/IEC 18013-5 for mobile
driving licenses, SD-JWT VC for selective disclosure, OpenID for Verifiable Presentations (OID4VP)
for presentation requests, OpenID for Verifiable Credential Issuance (OID4VCI) for issuance, and
the W3C Digital Credentials API for browser integration.
### What are the four layers of a credential ecosystem? [#what-are-the-four-layers-of-a-credential-ecosystem]
A credential ecosystem can be read in four functional layers. The issuing authority decides what
may be asserted and holds the revocation mandate. The issuance platform turns those instructions
into signed credentials. The trust framework defines accreditation, technical standards, and
obligations. The facilitation mechanism, or wallet, holds and presents credentials under the
citizen's control. Relying parties sit outside these four layers and are bound by general data
protection law.
### Does decentralized trust eliminate the need for governance? [#does-decentralized-trust-eliminate-the-need-for-governance]
No. Decentralized trust does not reduce the need for governance. It changes where governance is
applied and what instruments are available. Architectural safeguards do much of the work that
contractual rules did in earlier models, but policy is still needed for issuance terms,
accreditation, relying party obligations, equity, and enforcement.
### Can a decentralized credential be used offline? [#can-a-decentralized-credential-be-used-offline]
Yes. Because the credential carries a cryptographic signature, a verifier can confirm authenticity
offline without contacting the issuer for each transaction. Status checks for revocation may
require online connectivity depending on the design, but the core verification of issuer
authenticity and credential integrity can be performed locally.
# Decentralized Identifiers (DIDs)
URL: /docs/concepts/dids
## Overview [#overview]
Decentralized identifiers [(DIDs)](https://www.w3.org/TR/did-core/) are globally unique, highly
available and cryptographically verifiable digital identifiers. They are typically represented as a
Unique Resource Identifier (URI) that can point to a person, organization, data model or any
abstract entity.
The main difference between DIDs and traditional identifiers (e.g. email address or user account) is
that they are not owned by any service provider or organization. As such, they can be used across
platforms and prevent vendor lock-in.
DIDs are a [W3C standard](https://www.w3.org/TR/did-core/) and can be extremely effective at
preserving user privacy, enhancing transparency and consent, enabling data portability and enforcing
user control. DIDs can be used in identity management systems and provide superior security and
encryption compared to passwords by using public/private key pairs instead. Thus, DIDs offer a
different trust model to centralized identifiers. Specifically, DIDs form the basis of a
[Decentralized Public Key Infrastructure (DPKI)](https://github.com/WebOfTrustInfo/rwot1-sf/blob/master/draft-documents/Decentralized-Public-Key-Infrastructure-CURRENT.md)
for the web.
DIDs are classified according to their
[DID method](https://www.w3.org/TR/did-core/#dfn-did-methods). Each method defines a CRUD model to
describe how a specific DID scheme works with a specific
[verifiable data registry](https://www.w3.org/TR/did-core/#dfn-verifiable-data-registry) such as a
distributed ledger or blockchain. There are many
[dozens of DID methods](https://w3c.github.io/did-extensions/methods/#did-methods) that defined
their own specifications and contributed their DID scheme to the W3C.
DIDs are used through a process known as
[DID resolution](https://www.w3.org/TR/did-core/#resolution), which locates the registry where the
DID is anchored (based on its DID method) and retrieves its corresponding
[DID document](https://www.w3.org/TR/did-core/#dfn-did-documents). This is a JSON document that
contains cryptographic material such as public keys as well as ways to interact with the DID subject
via service endpoints.
## Structure [#structure]
DIDs are Unique Resource Identifiers (URIs) that follow the following pattern:
**`did:key` example**
```
did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5
```
## Methods [#methods]
Different DID methods have different properties and are better suited to some use cases over others.
There isn't a DID method that should be universally applied to every situation so getting a good
understanding of the different offerings will help you decide which is going to work best for your
use cases.
MATTR VII currently supports the following DID methods:
* `did:key` : The most basic type of DID. The public key forms the DID and has no further data
associated with it. **Example**: `did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5`
* `did:web` : This type of DID hosts requires hosting the DID document on a publicly accessible
domain in order to make the document and contents available. **Example**:
`did:web:learn.vii.au01.mattr.global`
### `did:key` [#didkey]
DIDs with a method of `key` are the most basic type of DID. The public key forms the DID and has no
further data associated with it.
MATTR VII automatically registers created DIDs when applicable. Any `did:key` will always have a
`registrationStatus` of `COMPLETED` as it is instantly available to be used and resolved.
#### Constraints [#constraints]
* This DID method does not support key rotation, and therefore there are limitations on using it
in a long-term setting. When keys need to be rotated for any reason, all credentials issued
using this DID must be revoked and reissued using the new key.
* This DID method does not advertise a service endpoint, where a DID document advertises a public
endpoint that could be used to send messages intended for the DID owner. As this DID method does
not support this capability, any messages will need to be routed by different means. MATTR VII
capabilities enable registering a `did:key` with a static inbox, so
that all messages intended for the DID owner are routed to that mailbox.
#### Key types [#key-types]
* Supported `keyType` for `did:key` are `Ed25519` and `Bls12381G2`.
* If the `keyType` is omitted, the default `keyType` is `Ed25519`. This `keyType` can be used as a
Verifier DID.
* If the `keyType` is set to `Bls12381G2` the created DID supports BBS+ signatures for creating
ZKP and selective disclosure enabled credentials.
* As `did:key` only support `Ed25519` or `Bls12381G2` key types, you can not use it to create CWT credentials. If you wish to create CWT credentials, create a DID using a keyType of
`P-256`, such as `did:web`.
* `Bls12381G2` cannot be used as a Verifier DID as it does not support symmetric key signing
required to verify messages.
### `did:web` [#didweb]
DIDs using the `web` method host the DID document on a publicly accessible domain in order to make
the document and contents available.
For example, MATTR has a `did:web` using our own domain `did:web:mattr.global`. This DID is hosted
on our website at `https://mattr.global/.well-known/did.json`. DIDs may also be hosted at specific
paths on your website.
#### Constraints [#constraints-1]
* `did:web`s inherently rely on the security of the website the DID Document is hosted on. We
would only recommend the use of this type of DID on trusted and known sites/domains, such as
government agencies and enterprises.
* Message signing is not possible at this time using DIDs that only contain a `bls12381g2` key
type. Note that MATTR VII creates `did:web`s with multiple key types by default to overcome this
constraint.
* When you create a new
[credential configuration](/docs/issuance/credential-configuration/overview) and don't specify an
explicit issuer DID, the most recently created `did:web` on your tenant is used as an issuer DID
by default.
#### Key types [#key-types-1]
MATTR VII creates all `did:web` with multiple key types by default. This enables issuers to issue
different types of credentials from a single DID, making use of the different key types features:
* `P-256` : This is the default option for signing [CWT credentials](/docs/concepts/cwt).
* `Bls12381G2Key2020` :
* Supports ZKP-enabled credentials.
* Supports key rotation.
* `X25519` suite:
* `Ed25519` : Recommended when the `web:did` is used as a Verifier DID, as it supports
symmetric key signing required for verifying messages.
#### Hosting [#hosting]
To make use of your `did:web`, you must make its DID document publicly available. It is important to
understand that verifiers will rely on accessing the DID document to verify credentials issued using
your did:web.
Therefore, the did.json file needs to be highly available. DID documents are not expected to change
very often, so caching is encouraged using standard caching headers. A global CDN is also highly
recommended to reduce latency during verification, as wallets may make multiple calls from across
the world (use-case dependent).
Furthermore, if the DID is tied to an issuing infrastructure, when the latter is taken down for
maintenance it will prevent both issuing and verifying during that downtime.
Taking these considerations into account, you can either host your did:web on your MATTR VII tenant,
or on your own domain.
#### Resolving [#resolving]
MATTR VII can resolve hosted `did:web`s by retrieving their hosted DID document. The tenant can
prove ownership of the keys associated with the DID Document through the
`https://{your_tenant_url}/.well-known/did-configuration` endpoint. The fact that the DID Document
is hosted on the domain links it to the DID.
## DID document [#did-document]
Every DID can be resolved to a DID document. A fully resolved DID document typically includes
information such as public keys, service endpoints and authentication mechanisms. It can include
multiple public keys of different types to support various cryptographic algorithms or use cases
```json title=DID document
{
"id": "did:web:learn.vii.au01.mattr.global",
"@context": [
"https://w3.org/ns/did/v1",
"https://w3id.org/security/suites/x25519-2019/v1",
"https://w3id.org/security/suites/jws-2020/v1",
"https://w3id.org/security/suites/ed25519-2018/v1",
"https://w3id.org/security/bbs/v1"
],
"keyAgreement": [
{
"id": "did:web:learn.vii.au01.mattr.global#FZXtUmNERo",
"type": "X25519KeyAgreementKey2019",
"controller": "did:web:learn.vii.au01.mattr.global",
"publicKeyBase58": "FZXtUmNERow4qGYb4LnLAVETodg7j7LrGyR78keGemWk"
}
],
"authentication": ["did:web:learn.vii.au01.mattr.global#Fo5mW6ivUV"],
"assertionMethod": [
"did:web:learn.vii.au01.mattr.global#z12L6Q6v",
"did:web:learn.vii.au01.mattr.global#Fo5mW6ivUV",
"did:web:learn.vii.au01.mattr.global#24QqAnwtn4"
],
"verificationMethod": [
{
"id": "did:web:learn.vii.au01.mattr.global#z12L6Q6v",
"type": "JsonWebKey2020",
"controller": "did:web:learn.vii.au01.mattr.global",
"publicKeyJwk": {
"x": "hLdNSMnIaT1h-fjft9zX-nd_khjG7LERGImyNhzAlqU",
"y": "ofu7wybOMA8ltzS3gYgdr0DMhlmJNmjhdYpwcHT1_O8",
"crv": "P-256",
"kty": "EC"
}
},
{
"id": "did:web:learn.vii.au01.mattr.global#Fo5mW6ivUV",
"type": "Ed25519VerificationKey2018",
"controller": "did:web:learn.vii.au01.mattr.global",
"publicKeyBase58": "Fo5mW6ivUVsVS8e6EWuTFguQxTGFDMAZ7JBLuVqPsf1e"
},
{
"id": "did:web:learn.vii.au01.mattr.global#24QqAnwtn4",
"type": "Bls12381G2Key2020",
"controller": "did:web:learn.vii.au01.mattr.global",
"publicKeyBase58": "24QqAnwtn4AkCwiJHyBBe1eiyo6UegLx6AK8c3XSDUnopaNJP3EZvzgn8fLTaSWFY9T59PxdMp5RqoBBpCg8nGUdJnTVcTX7BPs8ubu516CNxNUehzwyTYzNdRt6YVqQSRMw"
}
],
"capabilityDelegation": [
"did:web:learn.vii.au01.mattr.global#z12L6Q6v",
"did:web:learn.vii.au01.mattr.global#Fo5mW6ivUV",
"did:web:learn.vii.au01.mattr.global#24QqAnwtn4"
],
"capabilityInvocation": [
"did:web:learn.vii.au01.mattr.global#z12L6Q6v",
"did:web:learn.vii.au01.mattr.global#Fo5mW6ivUV",
"did:web:learn.vii.au01.mattr.global#24QqAnwtn4"
]
}
```
## Verification relationships [#verification-relationships]
Verification relationships are the building blocks in a DID document. They determine the
relationship between a key and the different capabilities it can be used for. DID documents include
the following verification relationships:
* `assertionMethod`
* `authentication`
* `keyAgreement`
For example, when a credential proof is created, the used private key will be linked to the
corresponding public key as referenced in the assertionMethod section. By resolving the DID document
for the Issuer DID it’s possible for anyone to validate the credential proof, verifying that the
Issuer has asserted the data in the credential to be true.
During a verification flow, these checks are handled automatically using secure industry-leading
methods, providing a simple result.
## DID URL [#did-url]
When referencing a specific sub-resource inside a DID document, we use the common
[fragment](https://www.w3.org/TR/did-core/#fragment) URLs pattern. When a DID key is specified in a
single string, this is usually known as a DID URL, as shown in the following example:
```
did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5#z6Mkn9kQbVXdeDW3h3GvYUV5BzTQDw5oh26CGDqS7sZq3kBN
```
Strictly speaking, any DID is a URL. However, we generally have used the term
DID URL to mean the more specific version, so in our [API
Reference](/docs/api-reference), `didUrl` would mean providing a value of the DID
with the # to reference an exact key.
## Usage [#usage]
MATTR VII tenants support managing DIDs, which includes creating, retrieving and deleting DIDs. When
creating a new DID, MATTR VII generates the required keys and metadata and registers the DID on a
public ledger (if applicable). When retrieving DIDs, the tenant resolves them to get the latest key
and service information.
MATTR VII establishes a verifiable relationship between your tenant domain and DIDs created by your
tenant, enabling linkage between an internet domain owner and a DID owner. This approach creates a
bridge that connects the traditional trust model of the internet with a distributed trust model.
This follows the
[Well Known DID Configuration](https://identity.foundation/.well-known/resources/did-configuration/)
open standard developed by the
[Decentralized Identity Foundation (DIF)](https://identity.foundation/).
## Common DID usages in MATTR VII [#common-did-usages-in-mattr-vii]
### Issuer DID [#issuer-did]
MATTR VII can be used by issuers to create DIDs that are used in CWT and Semantic CWT credential proofs. These are referred to as Issuer DIDs for these credential formats.
The [DID document](#did-document) must be publicly discoverable by verifiers so they can know that
the DID keys used for the credential proof are from an issuer they trust. This could be via a
government backed or large enterprise ecosystem, or another organization they already know and
trust.
Issuer DIDs private keys are stored in the MATTR VII Key Management System (KMS), and published on
the [DID Configuration endpoint](/docs/api-reference/platform/dids/wellKnownDidConfig). They
can be used to create different types of credentials, as well as support revocable credentials.
### Verifier DID [#verifier-did]
MATTR VII can be used by verifiers to create DIDs that are used in a credential presentation
exchange. These are referred to as Verifier DIDs. The credential holder needs to be sure that the
received presentation request is from a valid verifier before sending their personal credential data
in the response.
Even if other forms of trust are in place (e.g. the journey started from a website that the holder
trusts) the DID to domain linkage must still be verified to validate the domain against the values
used in the request.
Verifier DIDs private keys are stored in the MATTR VII KMS, and published on the
[DID Configuration endpoint](/docs/api-reference/platform/dids/wellKnownDidConfig). They must
support message signing or key agreement for encryption.
### MATTR wallets [#mattr-wallets]
When someone uses their MATTR wallet app to interact with a new domain (either issuer or verifier),
a unique DID is created for that interaction.
#### Subject DID [#subject-did]
Verifiable credentials that are linked to a specific holder (e.g. an education certificate) are
referred to as subject-bound credentials. When a DID from a wallet user is used in subject-bound
credentials, it is referred to as a *Subject DID*.
#### Holder DID [#holder-did]
When a DID is used to create presentations in response to verifier requests, it is referred to as a
*Holder DID*. MATTR VII generates a unique and new Holder DID for each issuer/verifier interaction.
If the verifier and the issuer are hosted on the same domain and using the
same MATTR VII tenant, the same DID will be used for both the Holder DID and
the Subject DID.
As part of the
[W3C Verifiable Credential data model specification](https://www.w3.org/TR/vc-data-model/#presentations)
the verifiable presentation model is used to wrap all credentials inside a presentation and apply
cryptographic proofs. The *Subject DID* from each credential is used to sign the presentation, as
well as the *Holder DID* (even if they are the same). The *Holder DID* is the only DID presented to
the verifier during DID auth presentation requests.
#### Public DID [#public-did]
DIDs enable sending messages and data directly to the DID owner, assuming the sender has a way of
retrieving the intended recipient DID. One such example is a Public DID that can be used to send
secure messages and issue credentials to a specific digital wallet that is linked to this DID.
# Credential formats
URL: /docs/concepts/formats-overview
MATTR credential formats combine and evolve with the latest standards and technology stacks to make
working with verifiable credentials seamless.
Selecting the right format for a solution depends on a number of factors, including but not limited
to:
* The size of the payload.
* The need for biometric or other identity assurance capabilities.
* The cryptographic scheme used.
* The need for privacy-preserving features like selective disclosure.
* The primary method of issuance, presentation and verification.
[Contact us](mailto:dev-support@mattr.global) if you need help deciding what
credential format is right for your use case.
MATTR platforms currently support the following credential formats:
* [mDocs](#mdocs)
* [CWT credentials](#cwt-credentials)
## mDocs [#mdocs]
mDocs are digital credentials based on the ISO/IEC
[18013-5](https://www.iso.org/standard/69084.html) standard and
[18013-7](https://www.iso.org/standard/91154.html) technical specification, designed to be stored on
a holder’s mobile device. mDocs support a variety of advanced security features, making them an
ideal choice for use cases requiring higher assurance identity credentials, such as driving licenses
or national IDs.
mDocs verification workflows can be carried out over
non-internet communication protocols such as BLE (facilitating in-person exchange and offline
verification), or via online presentation channels (facilitating online presentation flows). Both
these workflows support selective disclosure and enable authenticating the issuer, the holder, and
the device the mDocs are presented from.
Key architectures and technology stacks for mDocs:
* Based on the [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html) standard and the
[18013-7](https://www.iso.org/standard/91154.html) technical specification.
* Use CBOR for object representation and COSE for signing and encryption.
* Use X.509 certificates for implementing chain of trust authentication workflows.
* ECDSA with P-256 (ES256) algorithm support for issuer and device authentication.
Learn more about [mDocs](/docs/concepts/mdocs).
## CWT credentials [#cwt-credentials]
CWT credentials are best-suited for sharing authentic information simply. They carry a smaller
payload and are optimized for presenting in-person, whether affixed to a physical document or item
or displayed digitally on-screen.
Digital signatures ensure the authenticity of information included within the credential, however,
do not include assurance about the person presenting the information. If identity assurance is
needed, this can be done through attribute matching with an outside identity document.
Key architectures and technology stacks for CWT credentials:
* CBOR Web Token (CWT) data model.
* W3C Verifiable Credential (VC) JSON data model.
* NIST-approved `P-256` key types.
* COSE digital signature encoding.
Learn more about [CWT credentials](/docs/concepts/cwt).
## JSON credentials [#json-credentials]
JSON Credentials are based on the [W3C Verifiable Credential (VC) data model](https://www.w3.org/TR/vc-data-model/) for expressing cryptographically secure digital credentials on the web. VC provide a data sharing model where signed and linked data can be used to establish trust across contexts.
# Glossary
URL: /docs/concepts/glossary
This glossary is a collection of terms and definitions related to digital credentials, decentralized
identity, and the technologies and standards that support them. It is intended to help you
understand the concepts and terminology used in the MATTR ecosystem and the broader field of digital
credentials.
## A [#a]
**Assurance Level:** A measure of confidence in the authenticity of an identity or credential (often
called *Level of Assurance*, or *LoA*). Higher assurance levels indicate stricter verification and
greater trust in a credential’s validity, suitable for sensitive uses (e.g. a digital passport),
while lower assurance levels are acceptable for less sensitive uses (e.g. a loyalty card).
## B [#b]
**Bearer Credential:** A type of credential that grants access or rights to whoever possesses it,
without requiring proof of the holder’s identity. Similar to how cash works — anyone who has it can
spend it. In the digital world, bearer credentials are often easier to use but come with higher
risks if lost or stolen. MATTR's [CWT (CBOR Web Token) credentials](/docs/concepts/cwt) are an example of
bearer credentials; they can be presented and verified without requiring the verifier to confirm the
presenter’s identity, which is useful for certain offline or privacy-focused use cases.
## C [#c]
**Claim:** A piece of data or attribute about a subject (such as a name, age, or qualification) that
is asserted by the issuer in a credential. Multiple claims make up the content of a digital
credential, and each claim is attested to (signed) by the issuer. For example, a university degree
credential might contain claims about a person’s name, the degree earned, and the graduation date.
**Certificate Authority (CA)**: A trusted entity responsible for issuing, signing, and managing
digital certificates that bind cryptographic keys to identities (such as individuals, organizations,
or services). In the context of digital identity ecosystems (such as verifiable credentials), a Root
CA is the top-level authority in a certificate hierarchy. It signs the certificates of subordinate
CAs and acts as the ultimate trust anchor. All certificates and digital signatures validated in the
system must ultimately trace their trust back to the Root CA, which must be highly secure and
carefully governed to maintain ecosystem-wide integrity.
**Credential Issuance:** The process of creating a digital credential and delivering it to a holder.
During issuance, an *issuer* gathers the relevant information (claims) about the subject, formats it
into a credential, and cryptographically signs it. This digital signature ensures the credential’s
authenticity and integrity, allowing verifiers to confirm the credential without needing to contact
the issuer again.
**Credential Lifecycle:** The [sequence of stages](/docs/concepts/credential-ecosystem#credential-lifecycle) that a digital
credential goes through from creation to retirement. This typically includes *issuance* (credential
creation and signing by an issuer), storage and management of the credential by the *holder*,
presentation of the credential to *verifiers*, verification of its validity, and eventual expiration
or *revocation* if the credential becomes invalid. Understanding the lifecycle in terms of these
roles and steps is key to implementing secure credential solutions.
**Credential Offer:** An invitation from an issuer to a holder to receive a credential, typically
used in an interactive issuance flow. In this approach (for example, see MATTR's implementation of
the [OID4VCI](/docs/issuance/oid4vci-overview) workflow), the issuer provides an offer URL or token
describing the credential. The holder reviews the offer and, if they accept it, the credential is
then issued to their wallet.
**Credential Template (Schema):** A defined structure or blueprint for a credential that an issuer
sets up in advance. The template specifies what data fields (claims) the credential will include,
the format or schema for those fields, any rules such as validity period, and the cryptographic
details (e.g. which keys or signature algorithm to use). By using a template, issuers ensure that
all credentials of a certain type follow a consistent structure and meet the required standards.
MATTR VII implements credential templates using the
[Credential configuration](/docs/issuance/credential-configuration/overview) functionality.
## D [#d]
**Decentralized Identifier (DID):** A globally unique, persistent digital identifier that is not
tied to any central authority. A DID is typically represented as a URI string (for example,
`did:example:123456...`) and can refer to a person, organization, or thing. The main difference from
traditional IDs (like usernames or email addresses) is that DIDs are controlled by the user (through
cryptographic keys) and are not issued or owned by any single provider, making them portable across
different systems and preventing vendor lock-in. Each DID is associated with a document that
contains the public keys and other metadata needed to use the DID. [DIDs](/docs/concepts/dids) are used
in MATTR products as part of the [CWT](/docs/concepts/cwt) credential format.
**Decentralized Identity:** A paradigm for identity management in which individuals and
organizations can interact and share credentials without relying on a single centralized authority.
In a decentralized identity model, people have control over their own data and digital credentials,
typically stored in personal digital wallets, and they decide what information to share and with
whom. This approach enhances privacy for users (they only share necessary information) and can offer
organizations benefits in security and compliance, since trust is established through cryptography
and distributed networks rather than one central database.
**Digital Credential (Verifiable Credential):** A digital certificate or token that contains a set
of claims about a subject, digitally signed by an issuer so that it can be independently verified.
It is the electronic equivalent of a physical credential (like an ID card, license, or diploma),
designed to be tamper-evident and trustable. Because of the issuer’s cryptographic signature, anyone
(or any system) can cryptographically check that the credential was indeed issued by a legitimate
source and that its contents haven’t been altered.
**Digital Credentials API (DC API):** A standardized interface designed to streamline the process of
presenting and verifying digital credentials to web applications. The DC API is part of the
[W3C’s Decentralized Identity Working Group](https://www.w3.org/groups/wg/did/) and is intended to
facilitate the integration of digital credentials into web-based applications. It provides a set of
APIs and protocols that enable developers to easily implement credential presentation and
verification functionalities in their applications.
**Digital Identity:** The overall representation of a person, organization, or thing in the digital
world, made up of the attributes, identifiers, and relationships that distinguish them across the
systems they interact with. Digital identity is broader than any single credential. A credential is
one signed assertion of specific claims at a point in time, whereas a digital identity is the wider,
evolving collection of information and trust relationships that those credentials, identifiers, and
accounts contribute to. In a federated identity model, a subject’s digital identity is established and
recognized across multiple independent parties through agreed-upon trust relationships, so that an
identity managed by one provider can be relied upon by others without each party needing to hold all
of the underlying data. This federated approach contrasts with treating identity as a single
document or credential, and instead frames it as something asserted, verified, and reused across a
network of trusted participants. In decentralized models, the same broad notion of digital identity
applies, but control shifts toward the individual (see *Decentralized Identity* and
*Self-Sovereign Identity*).
**Digital Trust:** The confidence that digital interactions and transactions are secure, authentic,
and reliable. When digital trust is high, users and organizations believe that a person or
credential is who/what it claims to be and that data hasn’t been tampered with. Establishing digital
trust often involves technologies and frameworks (like verifiable credentials, digital signatures,
and trust frameworks) that ensure data integrity, privacy, and the legitimacy of all parties
involved in an online exchange.
**Digital Trust Service (DTS):** A service that acts as a neutral intermediary to establish trust
within a digital ecosystem. Instead of every participant needing to trust each other directly, all
participants trust the [Digital Trust Service](/docs/digital-trust-service), which in turn vouches
for the identity and integrity of participants and their credentials. This *proxy trust* model
simplifies interactions in large networks: the network operator maintains the trust relationships,
and each member just trusts the DTS to validate others. By offloading trust management to a DTS,
organizations can scale up federations of issuers and verifiers more easily while maintaining a high
level of assurance. MATTR solutions can include [Digital Trust Service](/docs/digital-trust-service)
capabilities to help organizations establish and manage trust in their ecosystems.
**Digital Wallet:** An application (typically on a smartphone or computer) that securely stores
digital credentials and the user’s cryptographic keys. A digital wallet allows a holder to organize
their credentials and control their use — for example, selecting and *presenting* a credential to a
verifier when required. It is analogous to a physical wallet but for digital identity documents: the
wallet safeguards credentials, often protecting them with passwords or biometrics, and enables the
user to share verified information with others in a privacy-preserving way.
[MATTR Pi SDKs](/docs/holding/sdk-overview) enable organizations build their own digital wallets, while
the [MATTR GO Hold](/docs/holding/go-hold/getting-started) white-label wallet solution
enables getting up and running quickly with limited development effort.
**Document Signer Certificate (DSC):** A specific end-entity X.509 certificate used to digitally
sign Mobile Security Objects (MSOs) within mobile documents (mDocs), as specified in the ISO/IEC
18013-5:2021 standard. Issued and signed by an Issuing Authority Certificate Authority (IACA), the
DSC ensures the integrity and authenticity of the mDoc's data. Each DSC includes a public key,
validity period, and signature from the IACA. When an mDoc is presented for verification, the DSC's
public key is used to validate the MSO's signature, confirming that the document has not been
tampered with and originates from a trusted source. Refer to the
[chain of trust](/docs/issuance/certificates/overview#document-signer-certificate-dsc) page for more information.
## H [#h]
**Hardware Security Module (HSM):** A physical computing device that securely generates, stores, and
manages cryptographic keys. It is used to perform sensitive operations such as digital signing,
encryption, and key protection in a tamper-resistant environment — ensuring high levels of security
for identity and credential systems.
Internal HSMs are built directly into a device (such as a mobile phone or secure element). These
HSMs manage keys locally and are tightly coupled with the hardware environment they protect. For
example, a mobile phone using an internal HSM might store credentials and perform cryptographic
operations without the keys ever leaving the device.
On the other hand, external HSMs operate outside of the main system (e.g., in a dedicated on-premise
appliance or cloud service) and is accessed over a secure channel. External HSMs are often used by
credential issuers, Certificate Authorities (CAs), or trust service providers to securely manage
high-value signing keys in centralized environments.
**Holder:** The person or entity in possession of a digital credential. The holder typically stores
credentials in their digital wallet and is responsible for deciding when and with whom to share
them. This role gives the individual control over their own digital identity information - the
holder can choose to present a credential (or only part of it, via selective disclosure) to prove
something about themselves, while keeping the credential secure and private when not in use.
## I [#i]
**Identity Assurance:** The level of confidence that an individual, organization, or object is who
or what they claim to be. This is established through processes like identity verification (e.g.,
checking passports, biometrics, or trusted data sources) during credential issuance. The higher the
assurance level, the more rigorous the checks to verify that the subject's identity is accurate and
trustworthy.
**Information Assurance:** In credential verification, information assurance means ensuring the
integrity and validity of the credential’s contents. This includes verifying that the credential’s
digital signature is valid (proving the data hasn’t been tampered with) and that the credential
meets expected format or schema requirements. It also involves checking that the credential is
currently valid - for instance, confirming it hasn’t expired and hasn’t been revoked by the issuer.
Together, these checks give the verifier confidence that the information presented is trustworthy
and up-to-date.
**ISO/IEC 18013-5:** An international standard defining how a mobile device presents a Mobile
Driver’s License (mDL) to a verifier in a close proximity (offline) scenario. It specifies the data
structure of the mDL and outlines key security features, allowing a verifier to validate the
authenticity and integrity of the license when shared from a digital wallet.
**ISO/IEC 18013-7:** An international technical specification that extends the mDL ecosystem to
support remote (online) presentation of an mDL. It defines protocols for verifying mDLs using a web
browser, including RESTful APIs and support for OpenID for Verifiable Presentations (OID4VP) and the
Digital Credentials (DC) API.
**ISO/IEC TS 23220 (series):** A developing series of international technical specifications
designed to broaden the use of secure mobile credentials beyond driver’s licenses. Referred to as
Mobile Documents (mDocs), the standards provide a framework for presenting and verifying various
types of digital credentials. ISO/IEC TS 23220-4 specifically builds on ISO/IEC 18013-5 and ISO/IEC
18013-7, adding features like holder authentication for a wider range of use cases.
**Issuer:** An organization or entity that creates and vouches for a credential. The issuer gathers
the relevant data about a subject, encodes it into a credential (following a template or schema),
and cryptographically signs the credential with its private key to ensure authenticity. Once issued,
the credential is delivered to the holder (for example, added to the holder’s wallet). Anyone who
later verifies the credential can check the issuer’s digital signature to confirm the credential
came from a trusted source and hasn’t been altered.
**Issuing Authority Certificate Authority (IACA):** A self-signed X.509 certificate that serves as
the root certificate in the chain of trust for mobile documents (mDocs), such as Mobile Driver's
Licenses (mDLs), as defined by the ISO/IEC 18013-5:2021 standard. The IACA identifies the mDoc
issuer and is used to sign subordinate certificates, specifically the Document Signer Certificates
(DSCs). These DSCs, in turn, sign the Mobile Security Objects (MSOs) within mDocs, ensuring the
integrity and authenticity of the credential's data. The IACA's self-signed nature means it is the
trust anchor; verifiers rely on its public key to validate the entire certificate chain down to the
presented mDoc. IACAs can have validity periods of up to 20 years, facilitating long-term trust in
the issued credentials. Refer to the
[chain of trust](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca) page for more
information.
## M [#m]
**mDL (Mobile Driver’s License):** A digital version of a driver’s license, designed to be stored on
a mobile device like a smartphone. An mDL contains the same personal information and driving
privileges as a physical license, but can be presented electronically. Modern mDL implementations
use encryption and device security (such as biometric locks and anti-cloning measures) to provide a
more secure, fraud-resistant alternative to a plastic ID card. They can also support privacy
features like selective disclosure (e.g. proving you are over 18 without revealing your exact date
of birth). mDLs are based on the ISO/IEC 18013-5:2021 standard, which defines the technical
specifications for mobile driver’s licenses and their secure storage and presentation. MATTR has
implemented mDLs as part of the [mDocs](/docs/concepts/mdocs) credential format.
**Mobile Security Object (MSO):** A structured, signed data object that provides cryptographic
integrity for the individual data elements within a mobile document (mDoc), such as an mDL, as
defined in the ISO/IEC 18013-5:2021 standard. The MSO is generated by the issuing authority and
digitally signed using the private key associated with a Document Signer Certificate (DSC). It
contains cryptographic hashes of each data element included in the mDoc (e.g., name, date of birth,
photo), ensuring that verifiers can confirm the authenticity of the data and detect any tampering.
The MSO also includes metadata such as the document type, version, and validity period, and is a
critical component in establishing trust in mDocs without requiring online connectivity. Refer to
the [chain of trust](/docs/concepts/chain-of-trust#mobile-security-object-mso) page for more information.
**mDocs (Mobile Documents):** A class of digital identity documents which expand the ISO/IEC
18013-5:2021 standard. mDocs are designed to be stored in a digital wallet on a mobile device and
can be verified either in-person or remotely. The key strength of mDocs is their ability to provide
strong authentication and high security; they are ideal for high-assurance identity credentials like
driver’s licenses, passports, or national ID cards. mDocs use a chain of trust (digital
certificates) and mechanisms like device binding to protect against forgery, cloning, and
impersonation, making them very robust for digital trust use cases. MATTR has implemented a
corresponding [mDocs](/docs/concepts/mdocs) credential format.
## O [#o]
**OpenID for Verifiable Credential Issuance (OID4VCI):** An
[open standard protocol](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html)
that adapts OAuth 2.0/OpenID Connect for issuing verifiable credentials. In an OID4VCI flow, an
issuer first provides a **credential offer** to the holder (often as a URL or QR code). The holder’s
wallet or app then uses an OAuth-like sequence to authenticate (if needed) and retrieve the
credential from the issuer’s server. This standard enables interoperability, allowing any compliant
wallet to obtain credentials from any compliant issuer in a secure and standardized manner. MATTR
has implemented [OID4VCI](/docs/issuance/oid4vci-overview) as part of MATTR VII's issuance capabilities.
**OpenID for Verifiable Presentations (OID4VP):** An
[open standard protocol](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html) that
enables the presentation of verifiable credentials using OpenID Connect-based flows. It allows a
holder to share credentials with a verifier in a privacy-preserving, secure, and standardized way —
often initiated by scanning a QR code or clicking a link. OID4VP helps enable interoperability
between different wallets, verifiers, and credential formats, and supports advanced features like
selective disclosure and subject binding. MATTR has implemented OID4VP as part of its
[mDocs online verification capabilities](/docs/verification/remote-overview).
**Original Equipment Manufacturer (OEM):** In this context, refers to a company that integrates
MATTR’s solutions into their own hardware or software products. For example, a point of sale device
might integrate MATTR’s credential verification capabilities to accept digital IDs or credentials
from customers. The OEM uses MATTR’s technology to enhance their product offerings, providing their
customers with advanced features like secure credential storage, verification, and presentation
without needing to develop these capabilities from scratch. This allows OEMs to leverage MATTR’s
expertise in digital identity while focusing on their core business.
## R [#r]
**Relying Party:** A term for an entity that depends on an outside credential or identity proof to
make decisions - essentially the same as a *verifier* in the context of digital credentials. The
relying party “relies” on the credential’s authenticity and integrity. For example, a bank’s website
acting as a relying party might accept a digital ID credential to onboard a new customer, trusting
that the credential is valid (It relies on the issuer and the credential’s verification checks
rather than collecting the data directly).
**Revocation:** The process of marking a previously issued credential as no longer valid or
trustworthy. An issuer may revoke a credential if, for instance, the credential’s subject no longer
meets certain conditions (imagine a professional license that gets revoked) or if the credential was
compromised. Revocation is typically implemented by adding the credential’s identifier or status to
a *revocation list* or status registry. When a verifier checks a credential, they can look up its
status on that list to see if the credential has been revoked, all without revealing which specific
credential is being checked (to preserve privacy). MATTR solutions support [revocation](/docs/issuance/revocation/overview) for
CWT and mDocs credential formats.
## S [#s]
**Selective Disclosure:** A privacy-enhancing feature that allows a credential holder to reveal only
the specific pieces of information required by a verifier, rather than sharing the entire
credential. For example, with selective disclosure, you could prove you are over 18 by sharing a
verified “over-18” attribute from your credential, without disclosing your full name or exact
birthdate. This is usually achieved with advanced cryptography so that the verifier can be sure the
undisclosed parts of the credential remain hidden and the revealed data is authentic. MATTR
solutions support selective disclosure for [mDocs](/docs/concepts/mdocs/core-capabilities#selective-disclosure).
**Self-Sovereign Identity (SSI):** An approach to digital identity in which individuals fully own
and control their personal identity information and credentials. In an SSI system, users (as
holders) receive verifiable credentials from various issuers (e.g. a government ID, a college
degree, a workplace ID) and store them in their personal wallet. The user decides which credentials
or claims to share with a verifier, without needing any central intermediary in the transaction. No
single authority “owns” the identity - it’s decentralized across many issuers and controlled by the
individual. This model often leverages DIDs and blockchain or other distributed ledgers to anchor
trust, enabling a decentralized yet trustworthy ecosystem for identity.
## T [#t]
**Trust Framework:** The set of governance rules, policies, and technical standards that define how
participants in a trust network operate and trust each other. A trust framework lays out, for
example, which issuers are recognized as authoritative, what credentials formats are acceptable,
what security and privacy standards must be met, and how disputes or failures are handled. It
provides the legal and procedural foundation for the network, ensuring that all members (issuers,
holders, verifiers) have a common understanding of the “rules of trust” they abide by. By adhering
to a shared trust framework, different organizations can accept each other’s credentials with
confidence.
**Trust Network:** A group or ecosystem of organizations and individuals that agree to mutually
recognize and accept digital credentials under a common trust framework. Within a trust network,
there may be many issuers, holders, and verifiers interacting across various use cases (e.g. a
network could include government agencies, banks, universities, and individuals all using the same
credential system). Because all participants follow the agreed rules and standards, a credential
issued by one entity (say, a government ID) can be trusted and verified by another entity (say, a
bank) without custom integration. In essence, the trust network ensures interoperability and trust
across organizational boundaries by establishing shared expectations and infrastructure for digital
trust.
## U [#u]
**Un-managed IACA:** A deployment pattern where the Issuer Authority Certificate Authority (IACA)
operates independently without centralized oversight or coordination (such as from a Digital Trust
Service). Organizations may choose this model when they want to control their own credential
issuance trust anchors, particularly in closed or private ecosystems. While this offers flexibility
and autonomy, it requires careful governance to ensure interoperability and trust.
## V [#v]
**Verifiable Presentation:** A package of information prepared by a holder that contains one or more
credentials (or selected parts of credentials), along with cryptographic proof that the holder
possesses those credentials. A verifiable presentation is typically created when a holder needs to
prove something to a verifier: the holder’s wallet collects the necessary data from their
credentials and signs the presentation to prove its authenticity. The verifier can then check the
signature and the included credential proofs to ensure everything is valid and was issued to that
holder. Verifiable presentations allow a holder to consolidate claims from different credentials and
present them in one go, with assurance that the data is authentic and belongs to the holder.
**Verifier:** The entity that requests and validates a credential presented by a holder. A verifier
checks that the credential was issued by a trusted issuer and that it hasn’t been tampered with or
revoked. This is done by cryptographically validating the issuer’s signature on the credential and
consulting any relevant status information (like an expiration date or revocation registry). If the
credential passes these checks, the verifier can trust the claims contained in it. In some contexts,
a verifier is also known as a *relying party* (because it *relies* on the credential’s authenticity
and the issuer’s trustworthiness).
**VICAL (Verified Issuer Certificate Authority List):** A consolidated list of trusted issuer
certificate authorities, used to simplify trust in ecosystems with many issuers. A VICAL is defined
in the ISO/IEC 18013-5 standard as a mechanism where a central authority (often a national Digital
Trust Service) collects and cryptographically signs a list of issuer CAs that are considered
trustworthy. By publishing this single, signed list, the VICAL makes it easy for verifiers to trust
credentials from any issuer on the list without needing separate agreements with each issuer. In
practice, if a verifier trusts the VICAL, they will trust any credential whose issuer’s root
certificate appears in the VICAL, greatly streamlining verification of credentials from multiple
jurisdictions or organizations. MATTR offers [VICAL](/docs/digital-trust-service/vical-overview) solutions as
part of its Digital Trust Service capabilities.
## Z [#z]
**Zero-Knowledge Proof (ZKP):** A cryptographic protocol that allows one party (the *prover*) to
prove to another party (the *verifier*) that a certain statement is true, without revealing any
additional information beyond the truth of the statement itself. In the context of digital
credentials, ZKPs enable a holder to convince a verifier of something about their data *without*
exposing the data. For example, a ZKP can allow you to prove “I am over 18 years old” by using your
credential, without ever showing your actual birth date. The verifier ends up convinced of the fact
(over-18) but learns nothing else. This technology is key to advanced privacy-preserving
interactions in decentralized identity systems.
# Identity proofing and holder binding
URL: /docs/concepts/identity-proofing-and-holder-binding
Description: How an issuing authority establishes confidence that a credential is issued to, and controlled by, the right person, and why that confidence can be established once and reused across the ecosystem.
Confidence in an authority-issued credential rests on a question that is easy to state and harder to
operationalize: how sure is the issuing authority that a credential belongs to the right person? In
a [decentralized trust model](/docs/concepts/decentralized-trust-model), the issuer is out of the
transaction loop once a credential is issued, so the assurance that matters most is established
before the credential ever reaches a relying party. The strength of that assurance is set during
issuance, not at presentation.
This page explains the three distinct moments where identity is verified, why holder binding is the
most consequential of them, and why confidence established once can be reused across the ecosystem
rather than re-litigated at every interaction.
## The three verification moments [#the-three-verification-moments]
Confidence in a credential depends on three separate verification moments, each serving a different
purpose and each governed differently.
| Moment | What happens | Who is responsible |
| ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| **Identity record creation or update** | Foundational identity proofing. The person is verified against authoritative records, with liveness confirmed where appropriate, establishing the record from which future credentials are derived. | Issuing authority |
| **Credential provisioning and holder binding** | The person requesting the credential is confirmed to be the person the identity record belongs to. This is the holder-binding event. | Issuance platform or wallet, on behalf of the issuing authority |
| **Credential presentation** | A relying party gains confidence that the person presenting the credential is its legitimate holder, often through device-level biometric access controls. | Wallet provider and relying party |
The first moment sets the **assurance ceiling**. The rigor of identity proofing at record creation
or update determines the maximum assurance any credential derived from that record can carry. No
later step can raise a credential above the assurance of the proofing that underpins it.
The second moment connects the credential to a person. A credential is only as trustworthy as the
process that binds it to its rightful holder during provisioning.
The third moment is where the holder demonstrates control at the point of use. For many
transactions, the biometric access controls already built into a modern device, such as facial
recognition or fingerprint authentication, provide sufficient assurance that the person presenting
the credential is its holder. Higher-risk use cases may justify additional verification, but the
need for it should be determined explicitly by the sensitivity of the credential and the nature of
the transaction rather than applied uniformly.
## Why holder binding is the pivotal decision [#why-holder-binding-is-the-pivotal-decision]
The most consequential identity assurance decision in the credential lifecycle is often not how a
credential is presented, but how confidently the issuing authority and its ecosystem partners bind
the credential to the correct individual during provisioning.
At the point of provisioning, a liveness check that confirms the person is physically present and
matches the identity record can significantly strengthen holder binding. This is a one-time event,
not ongoing surveillance. The biometric is used solely to confirm the binding and can be discarded
once the process is complete.
For credentials derived from high-assurance authoritative records, stronger holder-binding measures
may be appropriate. These can include document verification, facial matching against authoritative
records, and liveness detection. The objective is to ensure that a high-assurance credential is
issued only to the person entitled to receive it.
### Where holder binding takes place is a policy choice [#where-holder-binding-takes-place-is-a-policy-choice]
The key decision is not *whether* holder binding should occur, but *where*. The matching process
can be performed by the issuance platform before the credential is delivered, or by the wallet when
the credential is claimed. Both approaches can achieve the same assurance outcome. They differ in:
* **Where biometric processing occurs**, on a server controlled by the issuance platform or on the
holder's device.
* **Who controls the matching logic** and is accountable for its accuracy.
* **How responsibilities are allocated** across the issuing authority, issuance platform, and wallet
provider.
What matters is that the holder-binding event is performed, documented, and governed appropriately.
The choice between locations is a governance and privacy-design decision rather than a difference in
assurance.
## Biometrics belong to proofing, not to the credential [#biometrics-belong-to-proofing-not-to-the-credential]
This framing clarifies the role of biometrics in the ecosystem. Biometrics are most valuable as a
proofing and holder-binding mechanism, not as an attribute carried inside the credential. The
ecosystem gains assurance from the fact that a biometric check occurred, not from sharing biometric
information with relying parties.
A biometric template is a digital representation of a person's unique
physical characteristics, such as a face or fingerprint, that can be used to recognize them. Where
biometric processing is used for identity proofing or holder binding, only the outcome of that
process, such as *identity confirmed*, should be reflected in the credential. Raw biometric data,
facial images, fingerprints, and other reusable biometric information should not be credentialized.
This is consistent with the broader principle that several categories of information warrant
exclusion from default credential attributes. See
[Policy considerations](/docs/concepts/policy-considerations#what-should-and-should-not-be-credentialized).
## Establish confidence once, reuse it across the ecosystem [#establish-confidence-once-reuse-it-across-the-ecosystem]
The policy objective should be to establish confidence once and then allow that confidence to be
reused. Where high-quality identity proofing and holder binding have already occurred at issuance,
relying parties should not need to repeat identity verification unnecessarily. This reduces friction
for users, lowers compliance costs, and limits the collection and storage of personal information
across the ecosystem.
When the three verification moments work together, they establish that:
* the issuing authority knows who the individual is,
* the credential has been issued to the correct individual, and
* relying parties can trust the resulting credential without conducting their own extensive identity
verification.
This improves trust while reducing the overall privacy footprint of the ecosystem. It is also why
[selective disclosure](/docs/concepts/selective-disclosure) and derived assertions are so
effective: a relying party that can rely on a high-assurance, well-bound credential rarely needs the
underlying identity data at all.
## How this relates to the rest of the ecosystem [#how-this-relates-to-the-rest-of-the-ecosystem]
Identity proofing and holder binding sit upstream of almost every other governance question:
* They set the assurance level that [credential design](/docs/concepts/policy-considerations#what-should-and-should-not-be-credentialized)
can rely on.
* They underpin [lifecycle management](/docs/concepts/policy-considerations), because a credential
is only worth updating or revoking if it was bound to the right person in the first place.
* They interact with delegation and representation, where the person presenting a credential may not
be its subject. See
[Policy considerations](/docs/concepts/policy-considerations#delegation-and-representation).
The chain of cryptographic trust that lets a relying party verify *which issuer* signed a credential
is a separate question, covered in [Chain of trust](/docs/concepts/chain-of-trust). Identity
proofing and holder binding answer the complementary question of *which person* the credential
belongs to.
## Frequently asked questions [#frequently-asked-questions]
### What is the difference between identity proofing and holder binding? [#what-is-the-difference-between-identity-proofing-and-holder-binding]
Identity proofing is the process of verifying a person against authoritative records to establish or
update an identity record. Holder binding is the separate step of confirming that the person
requesting a credential is the person that identity record belongs to, so the credential is
provisioned to the correct individual. Proofing sets the assurance ceiling for any credential
derived from the record. Holder binding connects a specific credential to its rightful holder at the
moment of issuance.
### Should biometric data be stored in a verifiable credential? [#should-biometric-data-be-stored-in-a-verifiable-credential]
No. Biometrics are most valuable as a proofing and holder-binding mechanism, not as an attribute
inside the credential. Where a biometric check is used during issuance, only the outcome of that
check should be reflected in the credential, such as identity confirmed. Raw biometric data, facial
images, and fingerprints should not be credentialized, and the biometric used to confirm binding can
be discarded once the process is complete.
### Where should holder binding take place? [#where-should-holder-binding-take-place]
Holder binding can be performed by the issuance platform before a credential is delivered, or by the
wallet when the credential is claimed. Both approaches can achieve the same assurance outcome. They
differ in where biometric processing occurs, who controls the matching logic, and how
responsibilities are allocated across the ecosystem. The important policy requirement is that the
holder-binding event is performed, documented, and governed appropriately, not which actor performs
it.
### Do relying parties need to repeat identity proofing? [#do-relying-parties-need-to-repeat-identity-proofing]
Generally no. The policy objective is to establish confidence once and allow it to be reused. Where
high-quality identity proofing and holder binding have already occurred at issuance, relying parties
can trust the resulting credential without repeating identity verification. This reduces friction,
lowers compliance costs, and limits how much personal information is collected and stored across the
ecosystem.
# Concepts
URL: /docs/concepts
Concepts introduce the foundational ideas and frameworks that shape digital trust. In this section, we break down the core principles—such as identity, credentials, verification, and trust models—that underpin how secure, interoperable systems work. Whether you’re just getting started or deepening your understanding, these guides are designed to give you clarity and confidence in navigating the evolving landscape of digital trust.
The various MATTR platforms that offer digital trust capabilities.
The stages involved in the issuance, holding, and verification of digital credentials.
The key capabilities that enable digital trust across various platforms.
An overview of the different credential formats supported by MATTR.
The framework that ensures the integrity and authenticity of digital credentials.
Key terms and definitions related to digital trust and credentials.
# ISO/IEC 18013-5, 18013-7 and 23220 explained
URL: /docs/concepts/iso-mdoc-standards
Description: A plain-language guide to the ISO/IEC standards that underpin mobile documents - what 18013-5, 18013-7 and 23220 each cover, how they relate to each other, and which one applies to your use case.
If you are designing a digital identity program that involves mobile documents, you will
quickly encounter three closely related ISO/IEC standards: **18013-5**, **18013-7** and
**23220**. They are often discussed together, sometimes interchangeably, but they each
play a distinct role.
This page explains what each standard covers, how they relate to one another, and how to
decide which ones apply to your use case.
## The short version [#the-short-version]
* **ISO/IEC 18013-5** defines how a mobile driver's license (mDL) is structured and
presented **in person**, using short-range technologies such as Bluetooth Low Energy.
* **ISO/IEC 18013-7** extends that model to **online (remote) presentation**, so the same
credential can be verified over the internet or through a browser.
* **ISO/IEC 23220** generalizes the foundations of 18013-5 so they apply to **any mobile
document**, not only driver's licenses, and adds capabilities such as holder
authentication.
Together, these standards give issuers, holders, and verifiers a consistent framework for
high-assurance mobile documents across both in-person and online interactions.
## ISO/IEC 18013-5: the foundation [#isoiec-18013-5-the-foundation]
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html) is the foundational
standard for **mobile driver's licenses (mDLs)**. It was created to bring the same level of
trust as a physical driver's license into a digital, mobile form, while taking advantage of
the capabilities that mobile devices and cryptography offer.
It defines:
* The **data model** for an mDL, including the claims (such as date of birth, document
number, issuing authority) and how they are organized.
* The **mDoc credential format**, including how the credential is encoded (using CBOR) and
how it is signed (using COSE).
* The **in-person presentation protocols**, including how a wallet and verifier device
establish a session over Bluetooth Low Energy, Wi-Fi Aware, or NFC, and how the
presentation request and response are exchanged.
* The **trust model**, including how an issuer's signing certificate is anchored in an
Issuing Authority Certificate Authority (IACA) and how verifiers establish trust in a
given issuer.
* Privacy features such as **selective disclosure** through salted hashed claims, which
let a holder reveal only the specific claims a verifier requests.
The defining context for 18013-5 is **proximity**. The verifier and the holder are in
physical range of each other, and the credential exchange happens over short-range
protocols. Because the cryptographic verification does not require a live lookup against
the issuer, 18013-5 also supports **offline verification**, which is important for
roadside checks, border controls, and other field deployments.
## ISO/IEC 18013-7: extending to online [#isoiec-18013-7-extending-to-online]
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) extends the 18013-5 model
to **remote (online) presentation**. The credential format and trust model stay the same.
What changes is the interaction context: instead of a face-to-face exchange over
Bluetooth, the presentation happens over the internet, often through a browser.
18013-7 defines:
* Transport mechanisms for online presentation of mDLs and mDocs.
* Two protocol annexes built on widely adopted web standards:
* **Annex C** describes mDoc device retrieval over the
[W3C Digital Credentials API](/docs/verification/remote-web-verifiers/dc-api/overview),
currently supported on iOS and Safari.
* **Annex D** describes
[OpenID for Verifiable Presentations (OID4VP)](/docs/verification/oid4vp) over the
same Digital Credentials API, expected on Android and Chromium-based browsers.
* Session security and binding requirements that adapt the in-person protections of
18013-5 to online contexts.
The defining context for 18013-7 is **remote interaction**. A user opens a website, the
website asks for an mDL, and the wallet on the user's device responds, either on the same
device or across two devices (for example, scanning a QR code from a desktop with a
phone).
## ISO/IEC 23220: beyond the driver's license [#isoiec-23220-beyond-the-drivers-license]
The [ISO/IEC 23220 series](https://www.iso.org/standard/74910.html) takes the foundations
established by 18013-5 and **generalizes them**. It is built on the assumption that the
same trust, security, and presentation model used for mobile driver's licenses is valuable
for many other kinds of mobile documents, including:
* National identification cards
* Residence permits
* Vehicle registrations
* Professional licenses and permits
* Sector-specific credentials issued by government or regulated entities
ISO/IEC 23220-4 in particular is **compatible with 18013-5 and 18013-7** while introducing
additional capabilities, such as **holder authentication** (helping a verifier check that
the person presenting the credential is the rightful holder).
In other words, 18013-5 and 18013-7 give you the mDL. ISO/IEC 23220 gives you the
ingredients to build other mobile documents that behave consistently with mDLs but are
fit for a wider range of use cases.
## How the standards relate [#how-the-standards-relate]
The simplest way to think about the relationship is:
| Standard | Scope | Interaction context | Adds |
| --------------- | --------------------------------- | -------------------- | ---------------------------------------------------------------------- |
| ISO/IEC 18013-5 | Mobile driver's licenses (mDLs) | In person, proximity | mDoc format, in-person presentation, trust model, selective disclosure |
| ISO/IEC 18013-7 | mDLs (and mDocs based on 18013-5) | Online, remote | Online presentation over the DC API and OpenID4VP |
| ISO/IEC 23220 | Any mobile document | Both | Generalized model, holder authentication, broader credential types |
They are layered and complementary, not competing alternatives. 18013-7 builds on 18013-5,
and 23220 reuses the same building blocks while broadening the scope.
## Which standard do I need? [#which-standard-do-i-need]
A useful way to frame the question is to start from the **use case**, not the standard:
* **In-person verification of a driver's license on a phone.**
ISO/IEC 18013-5 is sufficient. This is the canonical mDL use case.
* **Online verification of a driver's license**, for example, opening an account or
proving age on a website.
You need ISO/IEC 18013-7 in addition to 18013-5.
* **Both in-person and online verification of a driver's license.**
You need both 18013-5 and 18013-7. Most production mDL programs will fall here.
* **Verification of mobile documents beyond driver's licenses**, such as national ID cards
or permits.
ISO/IEC 23220 provides the generalized foundation. In practice, deployments often
combine 23220 with 18013-5/7 building blocks.
If you are not sure which combination fits your program,
[contacting MATTR](mailto:dev-support@mattr.global) is a good way to talk through your
specific situation.
## Why this layered approach matters [#why-this-layered-approach-matters]
These standards exist because high-assurance identity is moving from a single channel
(physical card, in-person check) into many channels at once (mobile, online, cross-device,
cross-border). A layered set of standards makes this manageable:
* The **credential format and trust model stay constant** across in-person and online
contexts. An issuer signs an mDoc once, and that same credential can be presented in
proximity (18013-5) or online (18013-7).
* **Interoperability is preserved across vendors and jurisdictions.** A wallet and a
verifier built in different countries by different vendors can interoperate because they
follow the same standards.
* **New use cases can be added without reinventing the trust model.** When a program
decides to extend from driver's licenses to, for example, age verification credentials or
professional permits, ISO/IEC 23220 provides a path that reuses the same foundations.
## Frequently asked questions [#frequently-asked-questions]
### What is ISO/IEC 18013-5? [#what-is-isoiec-18013-5]
**ISO/IEC 18013-5** is an international standard that defines how a mobile driver's license
(mDL) is structured and how it is presented and verified **in person**, using short-range
protocols such as Bluetooth Low Energy, Wi-Fi Aware, or NFC. It is the foundational
standard for the mDoc credential format and underpins offline, proximity-based
verification.
### What is ISO/IEC 18013-7? [#what-is-isoiec-18013-7]
**ISO/IEC 18013-7** is a technical specification that extends ISO/IEC 18013-5 to **remote,
online verification**. It defines how an mDL or mDoc can be presented over the internet,
including how the request and response messages flow between a verifier on the web and a
wallet on a holder's device. **Annex C** of 18013-7 describes mDoc device retrieval over
the W3C Digital Credentials API, and **Annex D** describes OpenID4VP over the same API.
### What is ISO/IEC 23220? [#what-is-isoiec-23220]
**ISO/IEC 23220** is a series of standards that **generalizes** the foundations established
by ISO/IEC 18013-5 so they can apply to a **broader set of mobile documents**, not only
driver's licenses. It introduces additional capabilities such as **holder authentication**,
and is compatible with the data structures and security mechanisms defined in 18013-5 and
18013-7.
### What is the difference between ISO/IEC 18013-5 and ISO/IEC 18013-7? [#what-is-the-difference-between-isoiec-18013-5-and-isoiec-18013-7]
**ISO/IEC 18013-5** covers **in-person, proximity-based** presentation of mobile driving
licenses. **ISO/IEC 18013-7** covers **remote, online** presentation over the internet.
The two are complementary. They share the same credential format and signature model, but
address different interaction contexts. Most real-world deployments need both.
### Which standard do I need - 18013-5, 18013-7, or 23220? [#which-standard-do-i-need---18013-5-18013-7-or-23220]
If you only need in-person verification of a driver's license on a mobile device, **ISO/IEC
18013-5** is sufficient. If you also need to verify the credential online or in a
browser-based flow, you need **ISO/IEC 18013-7** as well. If you are working with broader
mobile documents beyond driver's licenses, such as national identification cards or
sector-specific permits, **ISO/IEC 23220** provides the generalized foundation. Most
production deployments draw on more than one of these standards.
### Are these ISO standards finalized? [#are-these-iso-standards-finalized]
**ISO/IEC 18013-5** was published in 2021. **ISO/IEC 18013-7** was published as a
technical specification in 2024 and as an international standard in 2025. Parts of
**ISO/IEC 23220** are published, with others still in development.
## Summary [#summary]
ISO/IEC 18013-5, 18013-7 and 23220 are not alternatives. They are layers of the same model
for high-assurance mobile documents. 18013-5 establishes the mDL credential format and
in-person presentation. 18013-7 extends it to online and browser-based verification. 23220
generalizes the foundation so the same model can carry other mobile documents beyond
driver's licenses. Most real-world deployments rely on more than one of these standards at
the same time.
# What is an mDL?
URL: /docs/concepts/mdl
Description: Learn what a mobile Driver’s License is, how it differs from a physical license, what it enables, and how ISO/IEC 18013-5 and ISO/IEC 18013-7 support trusted in-person and online verification.
A **mobile Driver’s License (mDL)** is a **driver’s license stored on a mobile device**, usually in a digital wallet app.
A true mDL is not just a photo or digital copy of a plastic card. It is a **standards-based digital credential** designed to let a person share license information securely and let another party verify that information with confidence.
mDLs are designed for both **in-person** and **online** verification. Standards such as **ISO/IEC 18013-5** and **ISO/IEC 18013-7** help make that possible by defining how mDL data is requested, presented, protected, and verified.
## In simple terms: what does an mDL do? [#in-simple-terms-what-does-an-mdl-do]
An mDL lets a person use their phone to prove information that would traditionally be shown using a physical driver’s license.
That can include proving:
* identity
* age
* driving entitlement
* address or other license-related attributes, depending on the use case and jurisdiction
The main idea is simple: instead of handing over a plastic card, the holder shares verified information from a digital credential.
## How is an mDL different from a physical driver’s license? [#how-is-an-mdl-different-from-a-physical-drivers-license]
A physical driver’s license is usually checked by visually inspecting it. A verifier looks at the photo, reads the details, and decides whether the card appears genuine.
An mDL works differently.
Because it is a digital credential built on technical standards, an mDL can support **cryptographic verification**. That means the verifier may be able to confirm:
* that the credential was issued by a trusted issuer
* that the data has not been changed
* that the credential is being presented in a secure session
* in some cases, that the credential is bound to the device presenting it
This gives an mDL capabilities that a physical card does not easily provide:
* stronger digital verification
* more privacy-aware data sharing
* online and remote presentation
* support for automated or semi-automated verification workflows
Another important difference is **data minimisation**. With a physical license, the whole card is usually shown even when only one fact is needed. With an mDL, a verifier can request specific data, and the holder can consent to sharing only what is required for the interaction.
## What interactions can an mDL enable? [#what-interactions-can-an-mdl-enable]
An mDL can support both **in-person** and **remote** identity checks.
### In-person use cases [#in-person-use-cases]
A mobile driver’s license can be used in situations such as:
* roadside checks
* age verification for restricted goods or venues
* proving identity at a service desk
* access to regulated services
* identity checks in hospitality, travel, or events
Some mDL workflows are designed to work **offline in real time**, which is useful where internet access is unavailable or unreliable.
### Online and remote use cases [#online-and-remote-use-cases]
An mDL can also support remote workflows, including:
* online account opening
* remote onboarding
* access to digital government services
* online age checks
* identity verification in financial transactions
These online interactions matter because many digital services still rely on scans, uploads, and repeated manual checks. A standards-based mDL provides a more structured and trustworthy way to exchange verified identity information online.
### Same-device and cross-device workflows [#same-device-and-cross-device-workflows]
Online mDL verification may happen:
* on the **same device**, where the user completes a transaction and presents the credential on one device
* across **multiple devices**, where one device is used for the service and another for the credential presentation
This flexibility supports a wider range of service designs and user experiences.
## What standards are used for mDLs? [#what-standards-are-used-for-mdls]
The two most important standards commonly discussed in relation to mDLs are **ISO/IEC 18013-5** and **ISO/IEC 18013-7**.
### ISO/IEC 18013-5 [#isoiec-18013-5]
**ISO/IEC 18013-5** defines how a mobile driver’s license can be presented and verified, especially in **proximity** or in-person scenarios.
It provides the technical foundation for:
* the structure of the credential
* the way data is exchanged
* the cryptographic mechanisms used to establish trust
* the ability for a verifier to validate the issuer and the credential data
In practice, this is what helps distinguish a real mDL from a simple digital copy of a license card.
### ISO/IEC 18013-7 [#isoiec-18013-7]
**ISO/IEC 18013-7** extends mDL capabilities to **remote or online verification**.
It supports the use of mDLs over internet-based interactions, helping organisations build workflows where license data can be requested and presented securely online. This expands mDL use beyond face-to-face presentation.
### Why these standards matter [#why-these-standards-matter]
These standards matter because they improve:
* **trust**, by defining how credentials are verified
* **interoperability**, by giving ecosystem participants a shared framework
* **scalability**, by reducing the need for bespoke approaches
* **consistency**, by aligning issuers, wallets, and verifiers around common models
## How do mDLs support privacy and security? [#how-do-mdls-support-privacy-and-security]
mDLs are often discussed in terms of both **security** and **privacy**, because they are designed to improve how identity information is shared.
### Security features [#security-features]
An mDL or mDoc-based credential can support:
* **issuer authentication**, so the verifier can check who issued the credential
* **device authentication**, which can help reduce risks such as cloning
* **holder authentication**, such as comparing the portrait to the presenter in person
* **session encryption**, which helps protect the exchange from eavesdropping in both in-person and online workflows
### Privacy features [#privacy-features]
An mDL can also support:
* **selective disclosure**, where only needed information is requested
* **holder consent**, where the person can see what is being asked for
* **reduced over-sharing**, compared with handing over a full physical card
This makes mDLs relevant not only for convenience, but also for better data handling practices.
## How does an mDL relate to mDocs? [#how-does-an-mdl-relate-to-mdocs]
A mobile driver’s license is commonly treated as a specific type of **mobile document**, or **mDoc**.
The broader mDoc concept refers to standards-based digital documents that can be stored on a mobile device and shared in trusted interactions. An mDL is one important example, but similar approaches may also be applied to other documents such as national IDs, health cards, certificates, and employee IDs.
In short:
* **mDL** = a mobile driver’s license
* **mDoc** = a broader category of mobile documents, which can include mDLs
## Why are mDLs important? [#why-are-mdls-important]
mDLs are important because people and organisations increasingly need to exchange identity information in ways that are secure, digital, and usable across channels.
### They bring trusted identity into digital channels [#they-bring-trusted-identity-into-digital-channels]
Many important interactions now happen online, but identity checking processes are often fragmented or weak. mDLs provide a path toward more reliable digital verification.
### They can improve user experience [#they-can-improve-user-experience]
Instead of repeatedly uploading scans or manually entering data, users may be able to share verified information more directly and consistently.
### They can reduce fraud and compliance risk [#they-can-reduce-fraud-and-compliance-risk]
Because mDLs support stronger verification and secure exchange, they can help organisations improve assurance and reduce some forms of misuse or tampering.
### They create a bridge between physical and online identity use [#they-create-a-bridge-between-physical-and-online-identity-use]
An mDL is familiar enough to be understandable to everyday users, but technically capable enough to support modern digital trust workflows. That makes it a practical entry point into broader digital credential ecosystems.
## Frequently asked questions [#frequently-asked-questions]
### What does mDL stand for? [#what-does-mdl-stand-for]
**mDL** stands for **mobile driver’s license**. It refers to a digital version of a driver’s license stored on a mobile device and designed for secure, standards-based sharing and verification.
### Is an mDL just a digital copy of a driver’s license? [#is-an-mdl-just-a-digital-copy-of-a-drivers-license]
No. A true mDL is not just a photo, screenshot, or PDF of a physical card. It is a digital credential designed to be verified using technical standards and cryptographic protections.
### Can a mobile driver’s license be used online? [#can-a-mobile-drivers-license-be-used-online]
Yes. Modern mDL implementations are increasingly designed to support **remote and online verification**, especially through standards such as **ISO/IEC 18013-7**.
### Can a mobile driver’s license be used in person? [#can-a-mobile-drivers-license-be-used-in-person]
Yes. In-person or proximity presentation is a core use case for mDLs, especially under **ISO/IEC 18013-5**.
### What is the difference between ISO/IEC 18013-5 and ISO/IEC 18013-7? [#what-is-the-difference-between-isoiec-18013-5-and-isoiec-18013-7]
**ISO/IEC 18013-5** focuses on how mDLs are presented and verified, especially in in-person interactions.
**ISO/IEC 18013-7** extends that model to support remote and online verification over the internet.
### Are mDLs more secure than physical driver’s licenses? [#are-mdls-more-secure-than-physical-drivers-licenses]
They can be more secure in important ways because they can support cryptographic verification, secure sessions, and more controlled data sharing. However, the exact security outcomes depend on the implementation, the issuer, the wallet, and the verifier setup.
### Do mDLs support selective disclosure? [#do-mdls-support-selective-disclosure]
Yes. One of the key benefits of an mDL is that a verifier can request only the data needed for a transaction, rather than requiring the holder to reveal the full contents of a physical card.
### Can mDLs work without internet access? [#can-mdls-work-without-internet-access]
Some in-person mDL workflows can support **real-time offline verification**, depending on the interaction model and implementation.
### Are mDLs the same as mDocs? [#are-mdls-the-same-as-mdocs]
Not exactly. An **mDL** is a specific kind of **mDoc**.
An **mDoc** is a broader category of mobile document that can include mobile driver’s licenses as well as other digital credentials.
### Why are people interested in mDLs now? [#why-are-people-interested-in-mdls-now]
Interest in mDLs is growing because organisations need better ways to support secure digital interactions across both physical and online channels, while also improving privacy, reducing fraud risk, and meeting rising expectations for digital services.
## Summary [#summary]
A **mobile driver’s license (mDL)** is a standards-based digital version of a driver’s license stored on a phone. Unlike a photo or digital copy of a physical card, an mDL is designed for secure, verifiable data sharing. It can support in-person and online identity checks, selective disclosure of information, and stronger privacy and security through standards such as **ISO/IEC 18013-5** and **ISO/IEC 18013-7**.
# mDocs beyond the driver's license
URL: /docs/concepts/mdocs-beyond-mdl
Description: How the foundations established by mobile driver's licenses (mDLs) extend to broader mobile documents such as national identification cards, residence permits, and professional licenses, and how ISO/IEC 23220 supports that transition.
The **mobile driver's license (mDL)** is often the first credential people encounter when
learning about mobile documents. That makes sense. mDLs are widely recognized, regulated,
and already in production in many jurisdictions.
But it is important not to mistake the starting point for the end state. The standards,
infrastructure, and user experience patterns developed for mDLs are designed to apply
to a much wider class of credentials: **mobile documents**, or **mDocs**. This page
explains how mDocs extend beyond the driver's license, the role ISO/IEC 23220 plays in
that extension, and why this matters for organizations planning digital identity
programs.
## The mDL as a starting point, not the end state [#the-mdl-as-a-starting-point-not-the-end-state]
There are good reasons that so many digital identity programs have begun with the mDL:
* **Familiarity.** A driver's license is a credential most people already understand and
carry today. Holders, verifiers, and operations teams can intuitively grasp what an mDL
is for and what it should look like.
* **Regulatory clarity.** Driver's licenses are issued under well-defined authorities, with
established processes for identity verification, issuance, and renewal. The governance
model translates relatively cleanly into a digital form.
* **Standards maturity.** [ISO/IEC 18013-5](https://www.iso.org/standard/69084.html) was
published in 2021 and gave the ecosystem a stable, internationally agreed standard to
build against. Wallets, verifier SDKs, and certification programs have been built
around it.
* **General-purpose identity utility.** In many jurisdictions, the driver's license is
already used informally as a general-purpose identity document. Putting it on a phone
is a natural extension.
Starting with the mDL lets issuers, wallets, and verifiers prove out their trust
infrastructure, wallet experience, and integration patterns on a credential everyone
already understands. It also produces a working ecosystem that the rest of a digital
identity program can plug into.
The mDL is, in other words, an excellent **first** mobile document. It is not meant to be
the **only** one.
## What does "beyond the driver's license" actually mean? [#what-does-beyond-the-drivers-license-actually-mean]
Once an issuer, wallet, and verifier are comfortable with mDLs, the same underlying
capabilities open the door to many other credential types. These include, for example:
* **National identification cards**: the closest natural extension of the mDL pattern,
often issued by the same or adjacent authorities.
* **Residence permits and travel documents**: credentials that today often involve
physical cards and in-person checks.
* **Vehicle registration and ownership documents**: credentials that benefit from being
presented alongside or independently of a driver's license.
* **Professional licenses and permits**: for example, regulated occupations such as
healthcare, law, transport, or trades.
* **Sector-specific government records**: hunting and fishing permits, firearms licenses,
social benefit entitlements, and so on.
* **Other high-assurance credentials** where strong issuer authentication, device
binding, selective disclosure, and offline verifiability are valuable.
All of these can be carried as mDocs because mDocs are not a driver's-license-specific
format. They are a general-purpose, high-assurance digital credential format that happens
to have been standardized first for driver's licenses.
## Why mDocs work well for credentials beyond mDLs [#why-mdocs-work-well-for-credentials-beyond-mdls]
The same properties that make mDocs a good fit for driver's licenses make them a good fit
for the broader class of mobile documents:
* **Strong issuer authentication.** Verifiers can cryptographically check who issued the
credential and that the data has not been tampered with. This matters as much for a
national ID or a professional license as it does for a driver's license.
* **Device binding.** The credential is bound to the holder's device, which makes
cloning much harder. For regulated credentials, this is often a hard requirement.
* **Selective disclosure.** A holder can share only the specific facts a verifier needs,
not the whole credential. A health professional might prove their registration status
without revealing every detail of their record.
* **Offline-capable verification.** Verification does not require a live call to the
issuer. This matters for field deployments such as border control, roadside checks, or
remote service delivery.
* **In-person and online presentation.** ISO/IEC 18013-5 handles proximity, ISO/IEC
18013-7 extends to online, and the same credential can flow through both. This is just
as useful for a residence permit as it is for a driver's license.
These properties are properties of the **format and trust model**, not of any one
credential type.
## How ISO/IEC 23220 enables the move beyond mDLs [#how-isoiec-23220-enables-the-move-beyond-mdls]
The standards community recognized early on that the model created for mDLs would need to
generalize. That is the role of
[ISO/IEC 23220](https://www.iso.org/standard/74910.html), a series of international
standards that generalizes the mDoc model.
ISO/IEC 23220:
* **Reuses the same building blocks** as ISO/IEC 18013-5 (CBOR encoding, COSE signing,
IACA-anchored trust, salted hashed claims for selective disclosure).
* **Decouples the model from the driver's license.** Instead of describing mDL-specific
data, 23220 talks about mobile documents in general.
* **Adds capabilities** such as **holder authentication**, which strengthens the link
between the credential and the person presenting it.
* **Stays compatible with 18013-5 and 18013-7**, so wallets and verifiers that already
support mDLs can extend to other mDocs without reinventing their infrastructure.
The takeaway is that ISO/IEC 23220 lets ecosystem participants reuse what they have built
for mDLs and apply it to a wider range of credentials. For a deeper look at how 18013-5,
18013-7 and 23220 fit together, see the
[ISO/IEC 18013-5, 18013-7 and 23220 explained](/docs/concepts/iso-mdoc-standards) page.
## What this means in practice [#what-this-means-in-practice]
For organizations planning a digital identity or credential program, the practical
consequence is straightforward: an mDL program can be designed as the **first step** in
a broader credential strategy, not as a standalone product.
That implies a few useful planning principles:
* **Choose infrastructure that is not mDL-specific.** Wallets, verifier SDKs, and trust
registries that only support mDLs will block future expansion. Look for solutions that
support mDocs in general, including 23220-aligned credentials.
* **Design issuance processes that can carry other credentials.** Many of the same
identity proofing, claims sourcing, and key management workflows that issue an mDL can
also issue other mDocs. Build them with that in mind.
* **Plan verifier acceptance broadly.** Verifiers that accept mDLs today can usually be
extended to accept other mDocs with relatively modest changes, provided the underlying
format support is in place.
* **Think about holder experience across credentials.** Holders will eventually carry
multiple mDocs in a single wallet. Wallet selection and credential presentation flows
should anticipate this.
## How MATTR supports mDocs beyond the driver's license [#how-mattr-supports-mdocs-beyond-the-drivers-license]
MATTR's mDoc format is **generic**. It is not restricted to driver's license claims. The
same MATTR VII issuance and verification capabilities used to support an mDL program
can also be used to issue and verify other mobile documents, including identity cards,
permits, and sector-specific credentials.
MATTR works with governments, ecosystem partners, and large organizations on credential
programs that go beyond mDLs. If you are planning a program that starts with the mDL
but anticipates other mobile documents over time, or that begins directly with another
mDoc type, [contact us](mailto:dev-support@mattr.global) to talk through the design.
## Frequently asked questions [#frequently-asked-questions]
### Are mDocs only for driver's licenses? [#are-mdocs-only-for-drivers-licenses]
No. The **mobile driver's license (mDL)** is the most established example of an mDoc, but
the underlying credential format, security mechanisms, and presentation protocols are
designed to carry a much wider range of mobile documents. **ISO/IEC 23220** generalizes
these foundations so they apply to any high-assurance mobile document.
### What kinds of credentials can be issued as mDocs beyond mDLs? [#what-kinds-of-credentials-can-be-issued-as-mdocs-beyond-mdls]
mDocs are well-suited to any high-assurance identity or entitlement credential that
benefits from cryptographic verification, device binding, selective disclosure, and
offline presentation. Examples include **national identification cards**, **residence
permits**, **vehicle registrations**, **professional licenses**, **sector-specific
permits**, and other government-issued records.
### Why is the mDL such a common starting point? [#why-is-the-mdl-such-a-common-starting-point]
Driver's licenses are widely recognized, regulated, and already used as a general-purpose
identity document in many jurisdictions. **ISO/IEC 18013-5** standardized them first, and
many governments have launched mDL programs as an entry point into broader digital
identity ecosystems. The mDL gives issuers, wallets, and verifiers a concrete first
credential to test patterns, infrastructure, and user experience before extending to other
mobile documents.
### What is ISO/IEC 23220 and how does it help? [#what-is-isoiec-23220-and-how-does-it-help]
**ISO/IEC 23220** is a series of international standards that generalizes the model
established by ISO/IEC 18013-5 so it can apply to **any mobile document**, not only mDLs.
It is compatible with 18013-5 and 18013-7 while introducing additional capabilities such
as **holder authentication**. This lets issuers reuse the same trust model, wallets, and
verification infrastructure across many credential types.
### Can MATTR help me extend an mDL program to broader mDocs? [#can-mattr-help-me-extend-an-mdl-program-to-broader-mdocs]
Yes. MATTR's mDoc format is **generic** and can carry the claims required for many
credential types beyond driver's licenses. MATTR works with governments and ecosystem
partners on programs that go beyond mDLs, including national IDs and other regulated
credentials. [Contact us](mailto:dev-support@mattr.global) to discuss your specific use
case.
## Summary [#summary]
The mobile driver's license is an excellent first credential, not the only credential.
The same standards, infrastructure, and user experience patterns built for mDLs apply to
a much broader class of mobile documents, from national IDs to residence permits to
professional licenses. ISO/IEC 23220 generalizes the foundations of 18013-5 so the same
trust model and tooling can carry these other credentials. For organizations planning a
digital credential program, the practical implication is to design with mobile
documents in general in mind, with the mDL as an early step rather than the destination.
# Platforms
URL: /docs/concepts/platforms
MATTR helps build trust into the digital world, ensuring that interactions remain seamless, secure,
and privacy preserving. By integrating our products with your procedures, policies and business
logic, you can streamline the management of digital credentials across their
[lifecycle](/docs/concepts/credential-ecosystem#credential-lifecycle). To support this, we offer
three key platforms:
* **MATTR VII** is a cloud-based platform offering scalable digital trust services through advanced
APIs and a web [Portal](/docs/platform-management/portal), catering to credential issuers,
verifiers and network providers.
* **MATTR Pi** provides SDKs for creating bespoke experiences or extending existing applications.
Ideal for those requiring cross-platform and cross-channel integrations.
* **MATTR GO** offers a quick-start solution with configurable white-label apps, perfect for
early-stage network operators, wallet providers and verifiers aiming for rapid deployment
without extensive resource allocation.
Each platform is designed to fit different needs within the digital trust ecosystem, helping you to
embed digital credentials into your solution.
## Choosing the right platform [#choosing-the-right-platform]
The three platforms address different layers of a credential ecosystem and most production
deployments use more than one of them together. The decision usually comes down to three
trade-offs: how much you want to build yourself, how distinctive the end-user experience needs to
be, and how quickly you need to reach production.
| If you need to... | Start with |
| -------------------------------------------------------------------------------- | ----------------------- |
| Issue, manage, or verify credentials as a service for your ecosystem | [MATTR VII](#mattr-vii) |
| Embed credential holding or verification into your own mobile or web application | [MATTR Pi](#mattr-pi) |
| Launch a branded holder or verifier app quickly without writing code | [MATTR GO](#mattr-go) |
## MATTR VII [#mattr-vii]
MATTR VII is the cloud platform that issuers, verifiers, and ecosystem operators build on. It
exposes the full credential lifecycle through REST APIs and a web
[Portal](/docs/platform-management/portal), and provides the services Pi SDKs and GO apps depend
on for backend operations.
**Who it is for**
* Government agencies and commercial credential issuers operating at scale.
* Relying parties running web or service-based verification.
* Ecosystem operators (trust framework providers, network coordinators) orchestrating issuers and
verifiers across a community.
**What it provides**
* [Credential lifecycle](/docs/concepts/capabilities) services for issuance, management, and
verification across multiple [credential profiles](/docs/concepts/formats-overview), including
mDocs (ISO/IEC 18013-5 and 23220) and CWT credentials.
* [Digital Trust Services](/docs/digital-trust-service) that expose signed lists of trusted
issuers and verifiers, including PKI for [chain of trust](/docs/concepts/chain-of-trust) and
trust registry capabilities across ecosystem members.
* [Platform management](/docs/platform-management), including
[custom domains](/docs/platform-management/custom-domain-overview), a web based
[Portal](/docs/platform-management/portal), and public key infrastructure.
Choose VII when the credential workflow itself, not just the user-facing application, is what you
need to operate. Issuance policy, revocation, trust configuration, and verifier resolution all
live here. Read the [MATTR VII spec sheet](https://files.mattr.global/specsheets/vii-overview.pdf)
for more.
## MATTR Pi [#mattr-pi]
MATTR Pi is a set of SDKs that embed credential holding and verification into your own mobile and
web applications. The SDKs abstract standards compliance, cryptography, transport flows, and
platform-specific behaviors so your team can focus on the user experience.
**Who it is for**
* Wallet providers building a distinctive brand experience, or integrating credentials into an
existing consumer app.
* Verifier organizations that need credential verification inside their own iOS, Android, or web
application (for example at a kiosk, point-of-sale, or front-of-house workflow).
* Development teams with the engineering capacity to own the application layer while leveraging
MATTR's standards implementation.
**What it provides**
* Holder SDKs for iOS, Android, and React Native to claim mDocs using [OID4VCI](/docs/issuance)
flows, store them on device, present them in person (per ISO/IEC 18013-5) and remotely (per
ISO/IEC 18013-7), and manage credential authentication policies (biometrics, PIN, passcode).
* Verifier mobile SDKs for iOS, Android, and React Native, supporting QR and NFC device
engagement and offline in-person verification per ISO/IEC 18013-5, and remote mobile-based
verification of same-device holder apps per ISO/IEC 18013-7 using OID4VP and the W3C
Digital Credentials API where supported.
* A verifier web SDK for browser-based remote verification using ISO/IEC 18013-7-aligned flows,
including OID4VP and the
[W3C Digital Credentials API](/docs/concepts/decentralized-trust-model#the-technology-that-enables-decentralized-trust)
where supported.
Pi works alongside VII, which provides the backend services for credential exchange, verification
policy, and issuer resolution. Read the spec sheets for the
[Holder SDK](https://files.mattr.global/specsheets/pi-overview.pdf),
[Verifier Mobile SDK](https://files.mattr.global/specsheets/pi-overview.pdf), and
[Verifier Web SDK](https://files.mattr.global/specsheets/pi-overview.pdf).
## MATTR GO [#mattr-go]
MATTR GO is a white-label app platform for organizations that need production-ready holder and
verifier apps under their own brand without commissioning custom development. Apps are built from
proven templates and configured for your environment, inheriting MATTR's underlying security
architecture and standards implementation.
**Who it is for**
* Government departments and commercial schemes launching credential-enabled apps.
* Organizations prioritizing time-to-market and a branded presence in app stores.
* Service providers that want a turnkey app with the flexibility to evolve later.
**What it provides**
* **GO Hold**: a branded holder app that claims, stores, and presents credentials in person and
remotely, with [selective disclosure](/docs/concepts/selective-disclosure), PIN or biometric
protection, and revocation handling.
* **GO Verify**: a branded verifier app for in-field validation, including offline operation and
visual signals for credential validity and expiry.
* Flexible service models: a *managed* model where MATTR delivers signed app bundles for you to
publish, or a *self-managed* model where MATTR signs and submits apps to Apple and Google
stores on your behalf.
* A design asset bundle covering icons, splash screens, typography, and layout templates.
Choose GO when the application layer is not where you want to invest engineering effort and you
need branded apps in users' hands quickly. Read the
[MATTR GO spec sheet](https://files.mattr.global/specsheets/go-overview.pdf) for more.
## Combining platforms [#combining-platforms]
The platforms compose rather than substitute. Common combinations include:
* **VII + Pi**: a custom wallet or verifier app built with Pi SDKs, backed by VII for issuance,
exchange, and verification policy. Typical when you need a distinctive end-user experience and
operate your own credential services.
* **VII + GO**: a branded white-label app backed by your own VII tenant. Typical when you want
both ecosystem services and user-facing apps under one operating model, without engineering the
apps themselves.
* **VII alone**: a backend-only deployment where end users interact through third-party wallets
and verifiers. Typical for public schemes (for example, an mDL program) where citizens use the
compliant wallet of their choice.
Choosing the right platform for you depends on your use case, requirements,
resources and implementation timelines. [Contact
us](mailto:dev-support@mattr.global) to discuss your options.
# Policy considerations for credential ecosystems
URL: /docs/concepts/policy-considerations
Description: How verifiable credentials reshape existing policy concerns, what the architecture handles structurally, what remains in the policy domain, and where to begin.
Verifiable credentials change the architecture of identity verification. They do not eliminate the
need for policy. They change where the policy work sits, which existing safeguards still do the
work, and which new questions arise that did not exist in the centralized model.
This page is written for policymakers, regulators, and issuing agencies thinking through the
governance of a credential ecosystem. It complements
[Decentralized trust model](/docs/concepts/decentralized-trust-model), which explains the
architectural shift, by focusing on the cross-cutting policy questions that follow from it.
The shape of these questions is consistent across jurisdictions. The answers are not, and should
not be. Constitutional structure, regulatory tradition, and the assurance profile of each credential
all matter. What follows is a map of the questions and the design choices each one opens up,
rather than a prescription.
## How the credential model reshapes existing policy concerns [#how-the-credential-model-reshapes-existing-policy-concerns]
The policymaker's task is not to evaluate verifiable credentials in isolation. It is to think
through how a credential ecosystem affects the concerns policy already addresses. The credential
model changes the shape of each one. Some get easier. Some get harder. Some change character
entirely.
### Privacy [#privacy]
A well-designed credential ecosystem is structurally more privacy-respecting than the centralized
model it replaces, but the gain depends on design choices that policy needs to enable and enforce.
[Selective disclosure](/docs/concepts/selective-disclosure) lets a verification answer a question
without revealing the underlying data. A bartender confirms a customer is over 18 without learning
the date of birth. A landlord confirms a tenant has the right to reside without learning the
immigration history. A bank confirms a customer's identity without storing a passport scan.
These outcomes are not the default. They are the result of the wallet, the credential, and the
verifier all being set up to support them. Where the design is right, the privacy gain is
substantial. Where the design is wrong, the credential model can be more privacy-harmful than what
it replaced, because high-assurance data flows in higher volumes through more pathways. The policy
task is to make sure the design is right.
### Data retention [#data-retention]
The traditional pattern was for relying parties to retain copies of identity documents as evidence
that a verification had been performed. The credential model makes that retention largely
unnecessary. The presentation produces a cryptographically signed proof that a relying party can
store as evidence the check was performed, without needing to store the underlying personal data.
Where retention is required, for regulatory compliance, dispute resolution, or statutory limitation
periods, it can be calibrated precisely to the use case rather than defaulting to "keep everything
indefinitely". Policy can codify this by setting maximum retention periods and requiring deletion
confirmation. Some jurisdictions are doing this through privacy law, others through trust framework
rules, others through contractual conditions of credential access.
### Security [#security]
Credentials are cryptographically signed and verifiable offline. This raises the bar significantly
above documents that can be forged, photoshopped, or stolen wholesale from a database. The shift
from "trust the document I am holding" to "trust the cryptographic signature" closes a category of
fraud that has been costly across every jurisdiction.
The new attack surfaces are different. They are concentrated at the issuance platform, which must
be hardened, at the wallet, which must resist device compromise, at the relying party software,
which must verify properly, and at the trust registry, which establishes who is allowed to issue
and verify what. These are smaller, more defined surfaces than the diffuse surface of "every
database holding a copy of every customer's documents". Security improves on balance, but the
security investment must shift accordingly.
### Fraud [#fraud]
The credential model materially reduces the scope for identity fraud. It also raises the stakes
when something does go wrong. A fraudulent government-issued credential is more valuable than a
fraudulent paper document because it carries higher assurance and is accepted in more places.
Issuance processes therefore need to be more rigorous than the analog equivalents, and revocation
needs to be operationally immediate when fraud is detected. The architecture supports this. The
issuing authority can revoke a credential in seconds, and the revocation propagates through the
ecosystem as relying parties check the credential's status. The policy work is to make sure the
revocation pipeline is in place before credentials are issued at scale. The rigor of issuance itself
depends on [identity proofing and holder binding](/docs/concepts/identity-proofing-and-holder-binding),
which establish that a credential is issued to, and controlled by, the right person.
### Data integrity [#data-integrity]
Credentials carry their own integrity guarantees. The cryptographic signature means a credential
cannot be altered without detection. The relying party knows the credential came from the named
issuer and has not been tampered with. This is a substantial improvement over current arrangements
where data is shared between systems through APIs or batch transfers and where errors propagate
silently. The credential becomes the canonical source of truth for the attribute it carries, at the
moment of presentation, with the issuer's authority cryptographically attached.
### Liability [#liability]
The decentralized model changes who is responsible for what, and the policy answer is not
automatic. In the centralized model, the agency that mediated a verification was generally
accountable for getting it right. In the decentralized model, the citizen initiates the sharing,
the wallet executes it, and the relying party makes the decision.
Where does liability sit when a fraudulent credential is accepted? When a legitimate credential is
rejected? When a relying party misuses received information? The general answer is that liability
follows the locus of action and obligation. The issuing authority is accountable for the accuracy
and integrity of what it issues. The wallet provider is accountable for executing presentations
faithfully and for the security of credentials in storage on the device. The relying party is
accountable for verifying properly and for using the received information only within the declared
purpose. Trust frameworks typically codify some of these allocations. Contractual terms of
credential access cover the rest. The transition period needs particular policy attention because
the old liability allocations no longer fit and the new ones are not yet familiar.
### Cost recovery [#cost-recovery]
The centralized model often depends on per-verification fees, such as a certified copy from a
registry, a check against an agency database, or a transaction through an identity verification
service. The credential model has different economics. The cost of issuance is incurred once. The
cost of verification is essentially zero per transaction.
This is generally welcome for citizens and relying parties. It can disrupt revenue lines for
agencies that have come to depend on per-check fees. The policy question is whether the new model
needs a different funding mechanism, such as per-issuance, subscription, or central appropriation,
and whether legacy fee structures need to be amended.
### Inclusion and equality [#inclusion-and-equality]
This is the concern that needs the most deliberate policy work, because the credential ecosystem
can magnify existing exclusion if it is not designed to address it. The model assumes a level of
digital literacy, device access, and institutional trust that is not evenly distributed in any
population. People without smartphones, people with low digital literacy, people with cognitive
disabilities, people experiencing housing instability, and communities with reasons to distrust
digital government services can all be left behind. When that happens, the failure tends to be
invisible to the agencies designing the system and visible to the community workers who absorb the
consequences.
Three policy responses help:
* **Accessibility design in the wallet itself**, going beyond formal compliance baselines.
* **Supported and delegated credential models**, where a trusted intermediary can help an
individual access and present their credentials without holding them.
* **Analog alternative channels** that are structurally maintained and funded, not aspirationally
committed to.
Equity has to be a design requirement, not an edge case.
### Regulation [#regulation]
Trust frameworks bring new regulatory functions into being. An accreditation authority assesses
providers against the framework. Standards bodies set technical requirements. Privacy regulators
retain their existing role over personal data handling. Sector regulators retain theirs over
regulated industries.
Most jurisdictions are running this as a multi-regulator model rather than creating a single new
regulator. The coordination among them matters more than the institutional design of any single
one. The relationship between the trust framework regulator, the privacy regulator, and any sector
regulators is the most important inter-agency relationship in the ecosystem, and it benefits from
being made explicit rather than left to emerge.
## What the architecture takes care of [#what-the-architecture-takes-care-of]
A recurring observation across credential programs is that architecture does more protective work
than regulation. Where the ecosystem is designed well, problematic data handling becomes
structurally difficult rather than merely legally prohibited. This is a meaningful shift, because
legal prohibitions depend on enforcement, and enforcement is always imperfect. Structural
prevention does not.
Several specific architectural features carry most of the load.
### Selective disclosure [#selective-disclosure]
A credential containing many attributes can be presented in a way that reveals only the ones needed
for the transaction. If the relying party never receives the date of birth, it cannot retain the
date of birth, share the date of birth, or have the date of birth stolen from its database. The
privacy outcome is achieved without depending on the relying party's compliance with retention
rules. The instrument exists in current standards (W3C VC Data Model, SD-JWT VC, ISO/IEC 18013-5)
and in all standards-compliant wallet implementations. It is the single most important protective
feature in the ecosystem. See [Selective disclosure](/docs/concepts/selective-disclosure).
### Purpose-bound presentation requests [#purpose-bound-presentation-requests]
OpenID for Verifiable Presentations (OID4VP) requires the relying party to declare upfront what
they are asking for and why before the wallet presents anything. This is a structural filter. What
cannot be requested cannot be received. It also creates an auditable record of the purpose for
which information was received, which makes downstream misuse easier to detect and harder to
defend. Mandating OID4VP in a trust framework turns over-collection from a rule violation into an
architectural impossibility.
### Cryptographic revocation [#cryptographic-revocation]
The issuing authority retains a fast, prospective instrument for invalidating credentials when
source data changes or misuse is detected. Where credentials are issued with appropriate expiry
dates and supported by status list infrastructure at the issuance platform, revocation propagates
through the ecosystem in seconds. This is materially stronger than the historic position where
revoked documents could circulate for months or years before downstream parties caught up.
Importantly, it is also more powerful than surveillance. The issuing authority does not need to
watch what happens at the relying party layer. It needs to be able to act decisively when it learns
something has gone wrong, and the architecture gives it that.
### Anti-phone-home wallet behavior [#anti-phone-home-wallet-behavior]
A wallet should not contact its supplier or the issuing authority every time a credential is
presented. The issuing authority therefore has no visibility into who is presenting credentials to
whom. This is not a gap. It is a feature. It prevents the wallet ecosystem from becoming a
surveillance infrastructure and makes it possible for citizens to use their credentials without
their government watching them do so. Trust framework rules in most jurisdictions explicitly
require this.
### Short expiry and reissuance [#short-expiry-and-reissuance]
Designing credentials with appropriate expiry windows means stored attributes become stale quickly.
A relying party that wants to rely on a credential attribute over time must reverify periodically
rather than treat the original presentation as a permanent record. This creates natural disposal
cycles and makes long-term retention less valuable.
### Coordinated disposal through revocation [#coordinated-disposal-through-revocation]
When an issuing authority revokes a credential, the revocation notification can be propagated to
relying parties bound by issuer terms, triggering contractual obligations to delete derived
attributes they have retained. This creates a coordinated disposal mechanism that has no analog in
the centralized model. The technical capability exists. The contractual framework that makes it
legally operative is the remaining work in most jurisdictions.
It helps to be precise about what revocation is. From a standards perspective, revocation is a
**status signal** that a credential should no longer be trusted. A revoked credential can still be
presented, but verification returns an invalid result, which prevents future successful use and
future collection of data through that credential. Revocation does not, by itself, force a wallet to
delete a credential, force a relying party to delete information already collected, or restrict a
service. Each of those downstream consequences is a separate policy decision that must be defined
explicitly through issuer terms, trust framework requirements, or relying party obligations. The
technical signal exists; its legal and operational meaning is a choice.
That choice should be made with parity to physical-document processes in mind. If a revoked physical
document had previously been sighted or copied, there would generally be no way to notify every
organization that saw it. Digital credentials make coordinated downstream action newly feasible, but
feasibility is not the same as obligation. Issuing authorities should apply coordinated consequences
where they are genuinely necessary and proportionate to credential sensitivity, rather than imposing
stronger retrospective obligations on digital interactions simply because the technology allows it.
### Zero-knowledge proofs [#zero-knowledge-proofs]
On a longer horizon, zero-knowledge proofs take the data minimization principle to its logical
endpoint. A credential can be used to produce a cryptographic proof that a condition is satisfied
without revealing any underlying attribute. The W3C Verifiable Credentials Data Model 2.0 supports
ZKP-compatible formats. This is the direction of travel for a category of high-volume,
low-sensitivity verifications, such as age confirmation, eligibility checks, and residency, where
even derived predicates may be more than the relying party needs.
The policy implication of all this is that architecture takes care of a great deal. Where
regulators in the centralized model spend significant effort trying to police downstream behavior
through retention rules, purpose limitation, and complaint-driven enforcement, the credential
ecosystem does much of that work structurally. Policy attention can be freed up for the questions
architecture cannot answer.
## What policy still needs to address [#what-policy-still-needs-to-address]
The architecture, however well designed, does not answer everything. Several categories of question
remain firmly in the policy domain. These are the issues most jurisdictions are still working
through.
### Trust framework scope and the relying party gap [#trust-framework-scope-and-the-relying-party-gap]
The architecture provides the tools. The trust framework decides who is bound by which rules. In
most jurisdictions, accredited credential services (issuers and wallets) are bound to a coherent
set of obligations. Relying parties typically are not, except through general privacy law. This is
the most consequential open policy question across credential programs.
The choices include:
* **Extending the trust framework** to bring relying parties inside it. A hard sell politically and
operationally.
* **Tiering accreditation** so that high-assurance credentials can only be received by accredited
parties. A workable compromise.
* **Imposing binding terms on relying parties as a contractual condition of credential access**.
Available immediately, but contractual rather than regulatory.
Most jurisdictions are landing on some combination of these. The right combination depends on the
assurance profile of the credentials being issued and the regulatory capacity of the jurisdiction.
There is no universal answer.
### What should and should not be credentialized [#what-should-and-should-not-be-credentialized]
Just because an issuing agency holds data does not mean that data should flow into a credential.
Several categories of information warrant exclusion from the default credential attribute set:
* **Ethnicity** is a special category under most privacy regimes, carries specific discrimination
risk, and should be excluded unless explicitly opted into for a defined purpose.
* **Sensitive relationship information** held in birth registers can expose third parties (parents,
siblings) who did not consent.
* **Detailed immigration status** can be misused in ways that affect employment, housing, and
access to services.
* **Health and disability inferences** should not appear in identity credentials regardless of
source.
* **Biometric templates** should not be credentialized. Only the outcomes of biometric verification
("identity confirmed: yes") should be.
* **Information the issuing agency holds but is not authoritative for** should be credentialized by
the authoritative source agency, not by the holder.
These are policy choices, not technology choices, and they need to be made explicitly at the
credential design stage.
### Inclusion architecture [#inclusion-architecture]
The technology cannot solve digital exclusion on its own. Policy needs to make explicit provision
for accessibility, supported credential use, delegated authority models (where a trusted
intermediary holds credentials for someone who cannot self-manage), and maintained analog channels.
The community organizations that absorb digital exclusion failures need to be in the design
conversation rather than discovered later as an edge case.
Equity impact assessments before each major rollout phase, public reporting on who is using the
credential ecosystem and who is not, and funded analog alternatives with service-level standards
are all useful instruments. None of these are technology decisions.
### Delegation and representation [#delegation-and-representation]
Much of the discussion around credentials assumes a person presenting a credential about
themselves. Many real-world interactions do not fit that assumption. Parents act for children,
guardians act for dependents, carers support vulnerable individuals, and authorized representatives
act under powers of attorney. These are not edge cases. They are common interactions across public
services, regulated sectors, and private service delivery, and a credential ecosystem needs to
support them from the outset rather than treating them as exceptions to be addressed later.
Technically, a credential is always issued to a specific person and represents information about
that person. Delegation introduces a distinction between the credential subject and the person
authorized to hold, manage, or present that credential. The technology can support this
distinction. The harder questions are legal and policy questions: who is permitted to act on behalf
of whom, under what authority, for what purpose, and for how long. Existing legal authorities, such
as guardianship arrangements and enduring powers of attorney, provide much of the answer, but policy
decisions are still required about which relationships the issuing authority will recognize, what
evidence establishes them, and how they are maintained over time.
Delegation is best treated as a lifecycle issue rather than a one-time decision. Authority may be
granted, modified, suspended, revoked, or expire. The transition to adulthood illustrates this
well: a parent may legitimately manage a child's credentials today, but that authority should not
continue indefinitely, and the policy framework should define when and how responsibility
transitions to the individual. Wallet design plays a role here too, since wallets may need to
support multiple credential subjects and clearly distinguish personal credentials from those held
under delegated authority.
Delegation also intersects with the relying party gap and with inclusion. A relying party may need
assurance not only that a credential is valid, but that the person presenting it is authorized to
act on behalf of the subject, communicated in a privacy-preserving way that does not require
excessive disclosure of personal or legal information. And because many individuals may never manage
credentials independently, delegation is one of the mechanisms that makes broad participation
possible. A system that assumes every person can self-manage digital credentials risks excluding
significant parts of the population. Delegated authority is therefore best understood as a
first-order design consideration rather than a feature to add later.
### Inter-agency coordination [#inter-agency-coordination]
Where the issuing authority and the issuance platform sit in different agencies, which is
increasingly common as governments separate identity policy from digital service delivery, the
coordination protocol between them needs explicit design. How does the issuing authority instruct
the platform to revoke a credential? On what evidence? Against what timing? What data sharing is
permitted between them? Who decides credential policy? Who maintains the trust registry?
These are not technology questions. They are inter-agency governance questions, and they are best
resolved before the credential program scales rather than after.
### Cross-border interoperability [#cross-border-interoperability]
No jurisdiction issues credentials only for use within its borders. Mobile driving licenses from
one jurisdiction are increasingly accepted at airports and other contexts in another. ISO/IEC
18013-5 provides the technical compatibility layer for mDL. The W3C VC Data Model and emerging
eIDAS-compatible formats provide it for broader credentials.
The policy work is still maturing. Bilateral and multilateral arrangements for mutual recognition,
joint accreditation of wallets and issuers, and shared assurance frameworks are emerging unevenly.
The technology is interoperable by design. The policy framework for cross-border recognition is the
remaining gap.
### Liability allocation during transition [#liability-allocation-during-transition]
During the period where both centralized and decentralized verification are operating in parallel,
policy needs to address what happens when something goes wrong in the new model, who is
responsible, and how recourse works. Trust framework rules can codify some of this. Contractual
issuer terms can address some. Sectoral regulation may need updates. Litigation will eventually
settle the rest, but policy work upfront reduces the cost of getting there.
### Enforcement and consequences [#enforcement-and-consequences]
Where general privacy law is the principal restraint on relying party behavior, jurisdictions are
realising that privacy law's enforcement architecture was designed for a different set of harms.
Many privacy regimes do not include meaningful financial penalties for ordinary breaches.
Enforcement is complaint-driven and retrospective. The credential ecosystem raises the question of
whether the enforcement floor is sufficient when high-assurance government-issued identity
information is involved. This is a legislative question in most jurisdictions.
### Platform layer governance [#platform-layer-governance]
Jurisdictions are taking different positions on wallet governance. Some operate a single state
wallet. Others have chosen explicit multi-wallet choice. Others are designing for federated wallets
across constituent jurisdictions. Each model has trade-offs:
* **Single state wallets** simplify governance and accountability but raise competition concerns
and create single points of failure.
* **Multi-wallet ecosystems** preserve choice and resilience but increase coordination complexity.
* **Federated models** distribute governance but require strong cross-jurisdiction agreement.
There is no single right answer. The policy choice is real and consequential and should be made
deliberately, with the trade-offs understood.
## A starting position for issuing agencies [#a-starting-position-for-issuing-agencies]
For an issuing agency considering whether and how to begin credentialling its identity holdings, a
few practical observations apply across the programs in production today.
### The technology is ready [#the-technology-is-ready]
Verifiable credential infrastructure has matured to the point that platform implementation is no
longer the bottleneck. Standards-compliant issuance platforms, wallets, and verification tools have
been deployed at scale in several jurisdictions. An agency starting now is not pioneering the
technology. It is making policy and design choices within a known technical envelope.
### The policy work is most of the work [#the-policy-work-is-most-of-the-work]
The harder questions are not "can we issue verifiable credentials" but "what should the credential
contain, who should be able to receive it, under what conditions, and what happens when something
goes wrong". These are decisions only the issuing agency and its regulator can make. They benefit
from being made early rather than late.
### Start with a defined use case [#start-with-a-defined-use-case]
The most successful programs have not tried to do everything at once. A focused initial use case,
such as a driving license or a single identity attestation, lets the agency develop the operational
muscle, including revocation, lifecycle management, and relying party coordination, before the
credential set expands.
### Build the governance instruments before scale [#build-the-governance-instruments-before-scale]
The technical platform can be implemented quickly. The credential policy, the trust registry, the
binding terms for relying parties, the revocation protocols, the coordination with the privacy
regulator, the equity impact assessment, the accessibility plan, and the inter-agency coordination
protocol all take longer and are harder to retrofit. The mistake to avoid is launching the platform
before the governance instruments are in place.
### Choose architecture over regulation where you can [#choose-architecture-over-regulation-where-you-can]
For every concern that arises, the first question is whether the architecture answers it. Selective
disclosure answers many privacy concerns. OID4VP answers many over-collection concerns.
Cryptographic revocation answers many accuracy concerns. Where the architecture answers a concern,
the policy task is to make sure the architectural choice is mandated rather than recommended. Where
the architecture does not answer the concern, policy needs to do the work. The discipline is to
keep the two questions in the right order.
### Plan for relying party governance from day one [#plan-for-relying-party-governance-from-day-one]
This is the area where almost every jurisdiction has had to come back and add governance later. The
architectural instruments (selective disclosure, OID4VP, short expiry) reduce the relying party
governance burden but do not eliminate it. Binding issuer terms imposed through the trust registry
as a condition of credential access are among the most powerful tools available within current law,
and they should be drafted in parallel with the credential design itself.
### Take inclusion seriously as a design requirement [#take-inclusion-seriously-as-a-design-requirement]
The programs that have succeeded for the broadest population have made inclusion a design
constraint from the outset, not an add-on. The programs that have struggled have learned the cost
later.
### Coordinate internationally where possible [#coordinate-internationally-where-possible]
ISO/IEC 18013-5, W3C VC Data Model 2.0, OID4VP, and SD-JWT VC are the technical lingua franca of
the global ecosystem. Aligning with these standards from the beginning protects against
jurisdictional lock-in and enables cross-border recognition later. Where bilateral or multilateral
relationships are particularly important, early coordination on accreditation and mutual
recognition pays off.
### Expect the policy framework to evolve [#expect-the-policy-framework-to-evolve]
Every credential program reviewed to date has revised its trust framework rules and accompanying
guidance multiple times as the program has matured. This is healthy, not a sign of failure. The
technology and the use cases are still developing, and the policy framework needs to evolve with
them. Build the policy infrastructure (the regulator function, the standards-setting body, the
consultation mechanisms) for ongoing evolution, not as a one-off settlement.
## Summary [#summary]
Verifiable credentials are not a magic privacy or security solution. They are an infrastructure for
moving identity verification from a model based on document exchange and centralized mediation to
one based on cryptographic proof and citizen control. They make some things structurally easier
(privacy, fraud reduction, data minimization) and some things newly important (issuer governance,
relying party accountability, revocation operations, equity by design).
The policy work is real, and it sits primarily with the issuing agency and the trust framework
regulator. The credential model is operational, the standards are mature enough to commit to, and
the policy framework can be built in stages alongside the technical rollout.
## Frequently asked questions [#frequently-asked-questions]
### Do verifiable credentials replace privacy law? [#do-verifiable-credentials-replace-privacy-law]
No. Verifiable credentials change where privacy protection is applied. Architectural features such
as selective disclosure, purpose-bound presentation requests, and anti-phone-home wallet behavior
do much of the work that retention rules and purpose limitation did under privacy law. But privacy
law still applies to relying parties that receive credentials and to any personal data they retain
after a presentation.
### Who is liable when something goes wrong with a verifiable credential? [#who-is-liable-when-something-goes-wrong-with-a-verifiable-credential]
Liability typically follows the locus of action and obligation. The issuing authority is
accountable for the accuracy and integrity of what it issues. The wallet provider is accountable
for executing presentations faithfully and for the security of credentials on the device. The
relying party is accountable for verifying properly and for using received information only within
the declared purpose. Trust frameworks usually codify some of these allocations, and contractual
terms of credential access cover the rest.
### Are relying parties bound by the trust framework? [#are-relying-parties-bound-by-the-trust-framework]
In most jurisdictions, accredited credential services such as issuers and wallets are bound to the
trust framework. Relying parties typically are not, except through general privacy law. Closing
this relying party gap is one of the most consistent open policy questions across credential
programs. Options include extending the trust framework, tiering accreditation, or imposing binding
terms on relying parties as a contractual condition of credential access.
### What data should not be put into a verifiable credential? [#what-data-should-not-be-put-into-a-verifiable-credential]
Several categories warrant exclusion from the default credential attribute set. These include
ethnicity, sensitive relationship information that can expose third parties, detailed immigration
status, health and disability inferences, and biometric templates. Information the issuing agency
holds but is not authoritative for should be credentialized by the authoritative source, not by the
holder.
### How should an issuing agency start a verifiable credential program? [#how-should-an-issuing-agency-start-a-verifiable-credential-program]
Start with a defined use case rather than trying to do everything at once. Build the governance
instruments, including credential policy, the trust registry, relying party terms, revocation
protocols, and the inclusion plan, before the platform scales. Choose architectural safeguards over
regulation where possible. Plan for relying party governance from day one. Coordinate
internationally by aligning with ISO/IEC 18013-5, the W3C VC Data Model 2.0, OID4VP, and SD-JWT VC.
### Do verifiable credentials risk excluding people without smartphones? [#do-verifiable-credentials-risk-excluding-people-without-smartphones]
Yes, if inclusion is not treated as a design requirement. The credential model assumes a level of
digital literacy, device access, and institutional trust that is not evenly distributed. Policy
responses include accessibility design in the wallet, supported and delegated credential models
where a trusted intermediary can help an individual present credentials, and analog alternative
channels that are structurally maintained and funded.
# Privacy in MATTR's architecture
URL: /docs/concepts/privacy
Description: How MATTR approaches privacy across issuance, holding, verification, and digital trust services, and how these principles are reinforced by the MATTR Security Framework.
Privacy is a foundational property of the MATTR product stack, not an afterthought layered on top.
The decentralized credential model that MATTR is built on shifts data control away from central
intermediaries and toward the holder. MATTR's job is to provide the infrastructure that makes this
shift practical at scale, while ensuring that the infrastructure itself does not become a new point
of correlation or data accumulation.
This page describes the principles that shape how MATTR thinks about privacy across the credential
lifecycle. For details on specific product areas, see the dedicated privacy pages for
[issuance](/docs/issuance/privacy), [holding](/docs/holding/privacy),
[verification](/docs/verification/privacy), and
[digital trust services](/docs/digital-trust-service/privacy).
## Privacy by design [#privacy-by-design]
Privacy by design means that privacy properties are built into the architecture of a system rather
than enforced through policy alone. In a federated identity model, the identity provider sees every
transaction. Privacy then depends on the provider's contractual obligations and audit posture. In a
decentralized credential model, the issuer is out of the transaction loop by design. The
architecture itself prevents the identity provider from observing how a credential is used.
MATTR products implement this principle across the credential lifecycle:
* During **issuance**, MATTR VII acts as a transit platform. Claims flow through the platform to
produce a signed credential, and the credential is delivered to the holder's wallet. Claim data
is not retained beyond what is necessary to complete issuance. Limited information can
**optionally** be retained to support credential lifecycle management.
* During **holding**, credentials live on the holder's device. MATTR Pi Holder SDKs and MATTR GO
use platform secure storage (Secure Enclave on iOS, Keystore on Android) to protect credentials
and keys at rest.
* During **verification**, the verifier checks cryptographic proofs locally. There is no
requirement to contact the issuer at presentation time, and no central party is informed that a
verification has taken place.
* In **digital trust services**, MATTR publishes trust information so that participants can
evaluate trust independently. The trust service does not sit in the transaction path and does
not see individual verifications.
The technical instruments that make this possible are described in
[What is a decentralized trust model?](/docs/concepts/decentralized-trust-model) and
[Selective disclosure](/docs/concepts/selective-disclosure).
## Data minimization [#data-minimization]
Data minimization is the principle that systems should collect, transmit, and retain only the data
strictly required for a defined purpose. Verifiable credentials make this practical in ways that
earlier identity models could not:
* **Selective disclosure** lets a holder reveal only the specific attributes a verifier needs (for
example, "over 18: yes") rather than the full contents of the credential. See
[Selective disclosure](/docs/concepts/selective-disclosure) for more.
* **Offline verification** removes the need for verifiers to contact the issuer at presentation
time. The issuer cannot observe verification activity.
* **Transit-only processing** is the default for MATTR VII. The platform does not persist claims
in issued credentials by default. Where persistence is needed for a specific business reason,
it is a deliberate, configurable choice made by the customer.
For developers building on MATTR, the practical implication is that data minimization is the path
of least resistance. Following the default patterns produces a minimal data footprint without
additional engineering work.
## Privacy backed by the MATTR Security Framework [#privacy-backed-by-the-mattr-security-framework]
Architectural privacy properties are necessary but not sufficient. They have to be reinforced by
the organizational controls that govern how MATTR builds, operates, and supports its products.
MATTR operates a Privacy Information Management System (PIMS) as part of the broader
MATTR Security Framework (MSF). The framework covers policies, procedures, roles, training, and
tooling across cyber security, data and privacy protection, prevention, enterprise risk, and
incident response. Independent assessments include:
* **ISO/IEC 27001:2022** certification, maintained on an annual assessment cycle.
* **SOC 2 Type 2** audit, performed annually by an independent auditor.
* **Annual external code audit** of the MATTR codebase by Trail of Bits.
* **Self-assessments** against ISO 27701, OWASP Top 10 Privacy Risks and ISO 29100.
The MATTR platform is designed to support customers meeting their own obligations under GDPR, UK
GDPR, the New Zealand Privacy Act 2020, the Australian Privacy Principles, California CCPA/CPRA,
Quebec Law 25, and Switzerland's revised FADP, among other regimes.
For full detail on policies, procedures, roles, retention schedules, sub-processors, and data
residency, see the published [Privacy Policy](/docs/resources/terms/privacy-policy) and the
[Data Processing Terms](/docs/resources/terms/data-processing-terms). MATTR's Data Management &
Protection Plan is available to customers on request via
[privacy@mattr.global](mailto:privacy@mattr.global).
## How privacy responsibilities are shared [#how-privacy-responsibilities-are-shared]
In a typical deployment, MATTR provides the credential infrastructure and the customer configures
it for a specific use case. This produces a shared responsibility model:
| Responsibility | MATTR | Customer |
| ----------------------------------------------------------------------- | ----- | -------- |
| Platform security, encryption, sub-processor controls | Yes | |
| Compliance certifications for the underlying platform | Yes | |
| Deciding what credentials to issue and what claims they contain | | Yes |
| Configuring user consent, retention settings, and verification journeys | | Yes |
| Publishing a privacy notice to end users | | Yes |
| Choosing the regional cloud deployment for data residency | Joint | Joint |
The architecture removes specific privacy risks (issuer-side surveillance, central correlation
across verifications) but it does not absolve customers of the responsibility to design their
issuance and verification flows in line with applicable privacy law.
## Frequently asked questions [#frequently-asked-questions]
### Where is customer data stored? [#where-is-customer-data-stored]
By default, MATTR does not store the personal data inside verifiable credentials. The MATTR VII
platform is transactional: claims pass through it during issuance to produce a signed credential,
and the credential is delivered to the holder's wallet. The platform retains a small amount of
metadata for audit and lifecycle operations (such as a hash of the issued credential and a
reference to the user it was issued to), but not the underlying claim values, unless the customer
has explicitly configured them to be persisted.
MATTR operates regional cloud deployments in the United States, Canada, Europe, Singapore,
Australia, and New Zealand. These regions are primarily where customer data is **processed** in
transit during issuance and verification, not where the personal data inside credentials is
stored long term. Where data is processed, it stays within the boundary of the chosen AWS region
except for specific, listed sub-processors that may operate in adjacent regions. The current list
of sub-processors is published at
[/docs/resources/terms/data-sub-processors](/docs/resources/terms/data-sub-processors).
### Is customer data encrypted? [#is-customer-data-encrypted]
Yes. Sensitive data is encrypted at rest using AES-GCM and in transit using TLS. All APIs exposed
by the MATTR platform require HTTPS. Access to encryption keys is treated as privileged access,
with MFA, monitoring, and alerting.
### Are credentials issued by MATTR VII privacy preserving? [#are-credentials-issued-by-mattr-vii-privacy-preserving]
The MATTR VII platform implements credential formats (mDocs, CWT, Semantic CWT) designed for
privacy-preserving use. mDocs support selective disclosure and offline verification, letting
holders share only the attributes a verifier asks for. Whether a specific deployment is privacy
preserving in practice depends on how the customer configures issuance, what claims they include,
and how verifiers consume the credential. The architectural building blocks support a minimal-data
design, and customer configuration determines the outcome.
### Does MATTR track how credentials are used after issuance? [#does-mattr-track-how-credentials-are-used-after-issuance]
No. Once a credential is issued to a holder's wallet, MATTR has no visibility into where, when, or
how it is later presented. The decentralized architecture makes this technically impossible from
the issuer side; the platform does not record or have access to verification events at third-party
verifiers.
### Can MATTR access the data inside a verifiable credential? [#can-mattr-access-the-data-inside-a-verifiable-credential]
Only in narrow circumstances. Claims pass through the platform during issuance to be signed into
a credential, and the resulting credential is delivered to the holder. Claims are not persisted by
default. Limited information may be retained for audit purposes (for example, a hash of the
issued credential), but this does not include the underlying claim values unless the customer has
explicitly configured them to be persisted.
### How does MATTR handle data breaches? [#how-does-mattr-handle-data-breaches]
A data breach is classified as a security incident under the MATTR Security Framework and is
managed through the documented Incident Management Procedure as a Severity 1 or Severity 2
incident. Customers are notified in line with their contractual terms and applicable regulatory
requirements. The Data Breach Procedure is part of the MSF and is reviewed on an ongoing basis.
### Where can I see the full list of sub-processors? [#where-can-i-see-the-full-list-of-sub-processors]
The published list is at
[/docs/resources/terms/data-sub-processors](/docs/resources/terms/data-sub-processors). It
includes the sub-processor's purpose, the data category processed, and the data location.
# What is selective disclosure in verifiable digital credentials?
URL: /docs/concepts/selective-disclosure
Description: Learn how selective disclosure helps people share only the information needed for a specific interaction, and why that matters for privacy, compliance, and trust.
**Selective disclosure** means a person can share **only the information needed for a specific interaction**, instead of handing over an entire credential or more personal data than necessary.
For example, someone may need to prove they are over 18, that their licence is valid, or that they are eligible for a service. In each case, the verifier may only need one fact or a small set of facts. Selective disclosure helps make that possible.
This matters because many identity checks still rely on collecting more information than is needed. That creates friction for users, increases privacy risk, and leaves organisations holding sensitive data they may not need to store.
## Why selective disclosure matters [#why-selective-disclosure-matters]
Selective disclosure helps shift identity checking from a **collect everything** model to a **request only what is needed** model.
That change has practical value for both sides of an interaction:
* the **holder** does not need to reveal unnecessary personal information
* the **verifier** does not need to collect, process, and protect data that is not relevant to the decision they are making
This can improve privacy, reduce compliance burden, and build trust in digital interactions.
Imagine a person buying an age-restricted product.
With a physical card, they may have to show:
* full name
* date of birth
* home address
* licence number
* photo
* card expiry
* other visible details
But for the transaction, the retailer may only need one answer:
* **Is this person over the required age?**
Selective disclosure makes it possible to share only what is needed for that decision, rather than exposing the full contents of the credential.
## What problem does selective disclosure solve? [#what-problem-does-selective-disclosure-solve]
In many identity workflows, organisations collect more data than they actually need.
That often happens because the easiest way to verify something is to ask for a full document, a scan, or a screenshot. But this creates several problems:
* people lose control over how much they share
* organisations collect extra personal data they must protect
* sensitive data can accumulate in systems that become attractive targets
* compliance obligations become heavier because more personal data is being handled
* customer trust can erode when requests feel excessive
Selective disclosure addresses this by allowing the data exchange to be more precise.
## Business value for the holder [#business-value-for-the-holder]
From the holder’s perspective, selective disclosure is valuable because it supports **privacy, convenience, and confidence**.
### Share less personal information [#share-less-personal-information]
A holder does not need to reveal their full address, licence number, or date of birth when a verifier only needs confirmation of age or eligibility.
### Keep more control over each interaction [#keep-more-control-over-each-interaction]
Selective disclosure gives people a clearer sense of what is being requested and why. That makes the interaction easier to understand and easier to trust.
### Reduce unnecessary exposure [#reduce-unnecessary-exposure]
The less personal data that is shared, the less personal data is exposed in each transaction. That can help reduce privacy risk over time.
### Improve digital experience [#improve-digital-experience]
When people can share only the needed information, digital interactions can feel more proportionate and less intrusive.
## Business value for the verifier [#business-value-for-the-verifier]
Selective disclosure also creates strong business value for the verifier.
This is important because privacy is not only a user concern. It is also an operational and compliance concern for the organisation receiving the data.
### Collect only what is needed [#collect-only-what-is-needed]
If the verifier only needs proof of age, licence validity, or eligibility, it does not need the rest of the credential.
This supports a more focused and efficient verification process.
### Reduce unnecessary data storage [#reduce-unnecessary-data-storage]
Collecting more data than necessary creates avoidable obligations. The more personal data an organisation gathers, the more it may need to classify, secure, govern, audit, and delete.
### Lower privacy and security risk [#lower-privacy-and-security-risk]
Large stores of personal data can become attractive targets. Selective disclosure helps reduce the chance of building unnecessary data “honeypots” that create extra risk and cost.
### Support data minimisation goals [#support-data-minimisation-goals]
Many privacy and compliance programs aim to limit collection to what is necessary for a specific purpose. Selective disclosure supports that principle in a practical way.
### Build trust with customers and partners [#build-trust-with-customers-and-partners]
Asking for less data can make an organisation appear more trustworthy and more respectful of user privacy.
## Real-world examples of selective disclosure [#real-world-examples-of-selective-disclosure]
Selective disclosure is easiest to understand through concrete examples.
### Age checks [#age-checks]
A retailer, venue, or online service may need to know whether a person is old enough for a restricted product or service.
In that situation, the verifier may only need:
* confirmation that the person is over the required age threshold
They may not need:
* full date of birth
* full address
* licence number
* other unrelated identity details
This creates a more privacy-conscious experience for the holder and reduces unnecessary collection for the verifier.
### Licence validity [#licence-validity]
A verifier may need to check whether a person holds a valid licence.
For example, they may need to know:
* whether the licence is valid
* what class of vehicle the person is entitled to drive
* whether the licence has expired
They may not need to collect:
* home address
* full identity profile
* unrelated credential data
This makes the interaction more relevant to the decision at hand.
### Proof of eligibility [#proof-of-eligibility]
A person may need to prove they are eligible for a service, discount, entitlement, or benefit.
In some cases, the verifier may only need:
* confirmation that the person meets the eligibility requirement
They may not need full background details about why the person qualifies.
This can help reduce oversharing in healthcare, public services, education, workforce, and financial use cases.
## Why this matters for privacy [#why-this-matters-for-privacy]
Selective disclosure matters for privacy because it supports a more proportionate way to exchange information.
Instead of assuming that every identity check requires a full document, it starts with a simpler question:
**What does the verifier actually need to know?**
That shift matters because privacy is often lost through routine over-collection, not just through major failures. When organisations repeatedly ask for full documents, users may end up sharing far more information than the transaction requires.
Selective disclosure helps reduce that pattern.
## Why this matters for compliance [#why-this-matters-for-compliance]
Selective disclosure also matters for compliance and governance.
When an organisation collects personal data, it takes on responsibility for handling that data appropriately. Even when collection is lawful, collecting more than needed can increase complexity.
A more selective model can help organisations:
* align verification with a defined purpose
* reduce unnecessary personal data flows
* simplify retention and deletion decisions
* reduce the scope of data handling controls
* support privacy-by-design approaches
In other words, asking for less data can mean less risk to manage later.
## Why this matters for trust [#why-this-matters-for-trust]
Trust grows when people feel an interaction is fair, transparent, and proportionate.
Selective disclosure supports that by helping users see that:
* the verifier is only asking for what is needed
* the transaction is not based on excessive data collection
* the organisation is taking a more privacy-aware approach
That can improve confidence in digital services, especially in sectors where identity checks are frequent or sensitive.
## Is selective disclosure only about privacy? [#is-selective-disclosure-only-about-privacy]
No. Privacy is a major reason it matters, but selective disclosure also helps with:
* **user experience**, by reducing friction
* **operational efficiency**, by narrowing the data being handled
* **security**, by reducing unnecessary data concentration
* **compliance**, by supporting data minimisation
* **trust**, by making requests feel more appropriate and transparent
It is best understood as a business capability with privacy benefits, not just a technical feature.
## How verifiable digital credentials support selective disclosure [#how-verifiable-digital-credentials-support-selective-disclosure]
Verifiable digital credentials make selective disclosure more practical because they are designed for structured, trustworthy data sharing.
In the mDocs model, the verifier can request only the information needed, and the holder can agree to share selected information from the credential.
This means the verifier does not always need the full credential. Instead, they can receive only the part that matters for the transaction, while still being able to trust what was shared.
The technical details behind that process are important, but the business outcome is simpler:
* the holder shares less
* the verifier collects less
* both sides still get a trustworthy result
## Why mobile credentials are especially useful here [#why-mobile-credentials-are-especially-useful-here]
Mobile credentials are well suited to selective disclosure because they support interactive digital exchanges.
Instead of handing over a full plastic card, the holder can respond to a specific request on their device. That makes it easier to shape the exchange around the purpose of the interaction.
This is one reason mobile credentials are increasingly relevant in:
* retail and hospitality
* government services
* financial services
* workforce and employment checks
* regulated access scenarios
Across these use cases, the common value is the same: more precise information sharing.
## What selective disclosure does not mean [#what-selective-disclosure-does-not-mean]
Selective disclosure does **not** mean the verifier gets no assurance.
It also does **not** mean that every interaction becomes anonymous.
Instead, it means the verifier receives the information needed for the decision they are making, without necessarily receiving everything else contained in the credential.
That distinction is important. Selective disclosure is about **precision**, not the absence of trust.
## A better model for digital trust [#a-better-model-for-digital-trust]
For many organisations, selective disclosure represents a better operating model for digital trust.
It allows them to move away from broad document collection and toward more targeted, purpose-specific verification.
That can help them:
* improve customer experience
* reduce unnecessary data handling
* lower privacy and security risk
* strengthen compliance outcomes
* build more trusted digital services
For holders, it offers a more respectful and practical way to prove something about themselves without exposing more than necessary.
## Frequently asked questions [#frequently-asked-questions]
### What is selective disclosure? [#what-is-selective-disclosure]
Selective disclosure is the ability to share only the information needed for a specific
interaction, instead of revealing an entire credential or full set of personal details. For
example, a holder can prove they are over a required age without revealing their full date of
birth, address, or licence details.
### Why is selective disclosure important? [#why-is-selective-disclosure-important]
It is important because it helps reduce unnecessary data sharing. That improves privacy for the
holder and reduces data collection, storage, and risk for the verifier. It also supports data
minimisation principles in privacy law and reduces the amount of personal data accumulating in
systems that could become attractive targets.
### What is an example of selective disclosure? [#what-is-an-example-of-selective-disclosure]
A common example is an age check. A verifier may only need confirmation that a person is over a
required age, rather than their full date of birth, address, and licence details. Another example
is a licence check, where a verifier may only need confirmation that a licence is valid, not the
entire licence record.
### How does selective disclosure help businesses? [#how-does-selective-disclosure-help-businesses]
It helps businesses collect less unnecessary personal data, reduce privacy and security exposure,
support data minimisation, and build trust with customers. Collecting less data also reduces
compliance burden under privacy regimes such as the GDPR, the Privacy Act, and similar frameworks.
### How does selective disclosure help users? [#how-does-selective-disclosure-help-users]
It helps users avoid oversharing, keep more control over their information, and complete identity
checks in a way that feels more proportionate and privacy-aware. People can confirm specific facts
about themselves without revealing unrelated personal details.
### Is selective disclosure only relevant for mobile driver’s licences? [#is-selective-disclosure-only-relevant-for-mobile-drivers-licences]
No. It is relevant across many verifiable credential use cases, including proof of eligibility, employee credentials, education credentials, health-related credentials, and other digital identity scenarios.
### Does selective disclosure reduce trust in the information being shared? [#does-selective-disclosure-reduce-trust-in-the-information-being-shared]
No. The goal is to reduce unnecessary data sharing while still allowing the verifier to trust the
information that is disclosed. Each disclosed attribute remains cryptographically signed by the
issuer and can be verified to the same standard as the full credential.
## Summary [#summary]
Selective disclosure allows a person to share **only the information needed for a specific interaction**.
That creates value on both sides:
* the **holder** avoids oversharing personal information
* the **verifier** avoids collecting data it does not need, reducing risk and compliance burden
For organisations building digital services, selective disclosure is not just a privacy feature. It is a practical way to improve trust, reduce data exposure, and create more proportionate verification experiences.
# What are verifiable credentials?
URL: /docs/concepts/verifiable-credentials
## What is a credential? [#what-is-a-credential]
A credential is evidence that proves something about a person. Every day, we use credentials to prove things about ourselves. Our identity, our qualifications, or our right to access certain services. A driver’s licence, a passport, or a student ID - these are all types of credentials.
In the real world, we’re familiar with physical credentials such as ID cards, membership/loyalty cards, property/ownership titles and others.
Typically we store these credentials in a safe place such as our wallet. This enables us to carry essential personal information and present it physically when needed to prove details about ourselves. For example, showing a driver's license to a police officer to confirm that we are authorised to drive that class of vehicle.
## Digital credentials [#digital-credentials]
As more of our everyday interactions shift online, it’s only natural that our credentials follow. People increasingly rely on mobile devices for everything, from banking and travel to accessing government services, and expect the same level of convenience when proving who they are or what they’re entitled to.
Digital credentials are the digital counterparts of the physical documents we use every day. They can be stored and presented through digital wallets, which function much like physical wallets but on your mobile device. A digital wallet enables you to carry, manage, and share your credentials electronically.
While this brings convenience, simply holding a digital copy of a document is not enough. A scanned ID or a digital “flash pass” might look official, but these copies are just as easy to forge or misuse as a paper document, and sometimes even easier.
In many cases, people use screenshots or low-security files to “prove” something about themselves, and these can be edited, duplicated, or faked with minimal effort. As a result, these basic forms of digital credentials cannot be relied upon for trust or security.
## Verifiable credentials [#verifiable-credentials]
That’s where verifiable credentials come in.
Verifiable credentials are digitally signed, tamper-evident digital statements that can be used to prove things like a person’s identity, age, affiliation, or qualification. They’re designed to be shared, checked, and trusted across both remote and in-person contexts.
Unlike static images or scans, verifiable credentials include cryptographic protections that allow others to independently verify their authenticity, ensuring that the credential hasn’t been altered and truly comes from a trusted issuer. They can be presented in a secure, privacy-preserving way and used to streamline interactions where trust is required.
Verifiable credentials can represent far more than identification cards. Some examples include:
* Mobile Driver's Licenses (mDLs): A Ministry of Transport can issue a digital Mobile Driver’s Licence (mDL) that citizens store in a mobile wallet and present during roadside checks or at rental car counters.
* Proof of income: A financial institution might issue a proof-of-income credential that customers can use to apply for loans across different banks.
* Educational qualifications: A university can issue digital degree certificates that graduates use to verify their qualifications with employers.
Today, when people refer to **digital credentials**, they often mean **verifiable credentials**, but it’s important to be clear about which type is being discussed. A simple digital copy and a cryptographically verifiable credential are very different in terms of security, trust, and reliability.
## Unique features of verifiable credentials [#unique-features-of-verifiable-credentials]
### The cryptographic foundations of trust [#the-cryptographic-foundations-of-trust]
Digital credentials, and specifically verifiable digital credentials, are the foundation of the MATTR product stack, enabling organisations and individuals to issue, hold, and verify credentials in a variety of real-world settings, from online services to mobile wallets to physical checkpoints.
Verifiable credentials use a fundamentally different trust model from traditional document-based verification, and the difference is easiest to see in how trust is established.
With traditional methods, trust is created through a point-in-time check against a central system. For example, an employer may manually review a PDF degree certificate and contact the issuing university to confirm it is valid, or a business may verify a licence by logging into a government portal or using a third-party verification service. These checks rely on indirect signals—such as whether a document number exists or whether a name and date of birth match a database—and assume that the underlying system is accurate and up to date. In practice, these systems can suffer from data gaps, update delays, and human error, making trust inherently probabilistic rather than guaranteed.
Verifiable credentials work differently. The proof of authenticity is embedded directly in the credential using cryptography. For example, a digital driver’s licence or university credential can be verified instantly by a trusted verifier without contacting the issuer or querying a central database. A cryptographic signature either matches the issuer’s key or it does not. A credential is either listed as revoked or it is not. The verification result is clear, unambiguous, and does not depend on fuzzy matching, manual review, or backend system availability.
This shift reduces manual effort, removes integration silos, and enables trust to move with the data rather than staying locked inside individual systems. It results in greater confidence, clearer outcomes, and a level of assurance that traditional verification alone cannot reliably provide.
### Inverting the trust model [#inverting-the-trust-model]
Traditional verification systems depend on checking information with a central authority each time it needs to be verified. For example, a verifier submits a name and document number to a government service and waits for a “yes” or “no” response.
This model has two key limitations:
* The verifier must trust the authority’s response without being able to independently verify the authenticity of the underlying credential.
* Every verification request is visible to the authority, allowing correlation of data checks and concentrating privacy and operational risk in a single system.
Verifiable credentials invert this model. Instead of asking a central service to confirm validity, the credential itself carries cryptographic proof of:
* Who issued it (issuer authenticity)
* What was issued (credential integrity)
* To whom it was issued (binding to the holder)
* Whether it has been altered, tampered with, or revoked
This proof is created using digital signatures based on asymmetric key pairs. A verifier can check the signature directly, without contacting the issuer, using publicly available keys.
This results in a stronger and more privacy-preserving form of trust. Trust no longer depends on a live lookup or a third party’s response. Instead, it can be independently and mathematically confirmed by the verifier, making trust deterministic rather than probabilistic. Verifiers do not have to assume that data is genuine. They can cryptographically prove it.
### Trusted issuers [#trusted-issuers]
Verifiable credentials still rely on a trusted issuer model, but trust is not based on brand recognition, assumed institutional authority, or static government lists. A verifier does not accept a credential simply because it carries the name of a well-known university, bank, or government agency, nor do they need to look that organisation up in a central registry at verification time.
Instead, trust is anchored in cryptographic keys and governed by explicit trust frameworks. Verifiers validate whether a credential was signed with a valid issuer key, whether that key is authorised under the relevant governance model, and whether it ultimately chains back to a recognised trust anchor. This allows trust to be evaluated consistently and independently, rather than inferred from reputation or jurisdiction.
Digital credential ecosystems can use hierarchical or mesh trust architectures. In a national system, trust may be anchored in a government root key. In an industry ecosystem—such as health or education—it may be anchored in a consortium or standards body. In cross-border or global use cases, multiple roots of trust can coexist, allowing credentials to be verified across jurisdictions without a single controlling authority.
In practice, this means a verifier can accept a digital licence or identity credential without prior knowledge of the issuing organisation. As long as the issuer’s key is trusted within the applicable framework and the signature is valid, the credential can be relied upon.
This creates a transparent, portable chain of trust that works consistently across organisations and jurisdictions, without relying on reputation or centralised control.
### Revocation and credential status [#revocation-and-credential-status]
Traditional verification typically provides a point-in-time yes/no result, with no artefact that can be independently checked later. For example, a verifier may confirm that an identity document or licence is valid at the moment of lookup, but once that check is complete, there is no portable proof of validity and no way to re-verify the result without repeating the process with the issuing authority.
Verifiable credentials work differently. They support built-in lifecycle and privacy controls such as revocation registries, status lists, short-lived credentials, and selective disclosure. In practice, this means a verifier can confirm that a credential is still valid, has not been revoked, and is within its intended lifetime—while the holder shares only the minimum information required, such as proving age eligibility without revealing a full date of birth.
Crucially, these checks can be performed without revealing the subject’s identity to the issuer or the wider network. For example, a verifier can confirm that a credential remains valid without notifying the issuing authority that a specific individual is being checked. This reduces correlation and surveillance risk while preserving strong security guarantees.
The result is a balanced model where security and privacy reinforce each other, rather than forcing organisations to trade one for the other.
### Integrity and tamper-proof [#integrity-and-tamper-proof]
Traditional identity documents—whether physical or digital—can be forged, altered, copied, counterfeited, or manipulated over time. For example, a PDF licence can be edited, a scanned passport can be reused, or a legitimate document can be presented by someone other than its rightful holder. While traditional checks reduce these risks, they do not eliminate them. Spoofing, synthetic identities, and presentation attacks remain persistent challenges, especially at scale.
Digital credentials address these weaknesses at the architectural level. Any modification to a digital credential—even changing a single bit—immediately invalidates its cryptographic signature. This makes tampering and forgery detectable by default, rather than something that must be inferred through visual inspection or backend checks.
As a result, organisations gain strong assurance at the moment of verification that the data is authentic, unaltered, and issued by a trusted authority. Verification requires no specialist equipment or proprietary systems—only cryptographic validation—making security inherent to the credential rather than dependent on perimeter controls.
| Fraud type | Real-world example | How verifiable credentials resist it |
| ----------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| Document forgery | A fake university degree created using design tools | The credential cannot be forged without the issuer's private key; invalid signatures are immediately rejected |
| Document tampering | Editing a name or expiry date in a PDF licence | Any change invalidates the cryptographic signature, making tampering instantly detectable |
| Copy or reuse | Reusing a scanned ID or screenshot across multiple services | Credentials can be bound to the holder and verified using challenge-response, preventing reuse |
| Counterfeiting at scale | Mass-produced fake IDs that "look right" | Visual appearance is irrelevant; only valid cryptographic signatures are accepted |
| Presentation attacks | Using someone else's legitimate document | Holder binding and selective disclosure ensure the presenter controls the credential |
| Insider manipulation | An internal operator alters records in a backend system | Trust is based on signed credentials and governance rules, not mutable databases |
| Centralised data breach | Large identity databases compromised and reused | No central store of personal data is required for verification |
Digital credentials do not merely reduce fraud risk. They make entire classes of attacks cryptographically infeasible. This gives organisations assurance at the moment of verification that the data is authentic and unaltered.
### Privacy by design [#privacy-by-design]
One of the most significant security advantages of digital credentials—often overlooked—is what does not need to be shared.
Traditional verification typically requires uploading full documents, exposing more personal data than necessary, and relying on third-party intermediaries to store, process, or validate that information. For example, proving age may involve sharing a complete identity document that reveals a full name, date of birth, address, and document number—far more than the transaction requires.
Digital credentials enable selective disclosure. A holder can prove a specific fact—such as being over 18—without revealing their exact birthdate or any unrelated attributes. The verifier receives only what is needed to make a decision, nothing more.
This approach significantly reduces data exposure, shrinks the attack surface, limits correlation across transactions, and avoids long-term retention of sensitive personal information.
Security is strengthened by minimising what organisations need to collect and hold, not by accumulating more data.
### Empowering the holder [#empowering-the-holder]
Unlike traditional methods, where verification is performed on the user, digital credentials place the user at the centre of the interaction.
The user holds their credentials, decides when and where to share them, and controls exactly what information is disclosed. Verification happens locally, allowing verifiers to confirm authenticity without broadcasting the user’s activity to central systems or third parties.
The result is a model of freedom through trust: strong assurance for organisations, and genuine agency for individuals—where security and empowerment reinforce each other rather than compete.
## Verifiable credentials in production [#verifiable-credentials-in-production]
Verifiable credentials are no longer an emerging technology. National and state-level programs are
in production or in active rollout across multiple jurisdictions, each making different design
choices that reflect its constitutional structure, regulatory tradition, and policy priorities.
* **European Union.** All member states are required to provide a citizen-accessible EU Digital
Identity Wallet, with private sector acceptance obligations following. The regulatory framework
(eIDAS 2.0) and the technical framework (the Architecture and Reference Framework with multiple
implementing acts) define federated, supranational standards that each member state implements.
* **United Kingdom.** The UK has chosen a single state wallet, the GOV.UK Wallet, with a digital
Veteran Card already live and a mobile driving licence in phased public rollout.
* **Australia.** Each state runs its own mobile driving licence program aligned with the ISO/IEC
18013-5 standard, and population coverage across the country is high. The federal Digital ID Act
2024 sets a trust framework on top of these state-level credentials.
* **United States.** Some state DMVs have launched mobile driving licence programs that allow
storage across multiple wallets, including state-issued, Apple Wallet, Google Wallet, and Samsung
Wallet, reflecting an explicit multi-wallet choice posture.
* **Singapore.** Singpass, a long-established national digital identity used across thousands of
agency and business services, is being extended with verifiable credential capabilities on top of
its centralized identity foundation.
These programs are not converging on a single model. They are converging on a shared technical
floor (verifiable credentials, ISO/IEC 18013-5, OID4VP, selective disclosure) and on a shared set
of policy questions about how to govern the resulting ecosystem. See
[Policy considerations](/docs/concepts/policy-considerations) for those questions and the design
choices each one opens up.
## Frequently asked questions [#frequently-asked-questions]
### What is a verifiable credential? [#what-is-a-verifiable-credential]
A verifiable credential is a digitally signed, tamper-evident digital statement that can be used to prove things like a person's identity, age, affiliation, or qualification. Unlike static images or scans, verifiable credentials include cryptographic protections that allow others to independently verify their authenticity.
### How are verifiable credentials different from traditional digital documents? [#how-are-verifiable-credentials-different-from-traditional-digital-documents]
Traditional digital documents like PDFs or scanned IDs can be easily forged or edited. Verifiable credentials embed cryptographic proof of authenticity directly in the credential, allowing verifiers to confirm validity without contacting the issuer or querying a central database.
### Can verifiable credentials be forged or tampered with? [#can-verifiable-credentials-be-forged-or-tampered-with]
No. Any modification to a verifiable credential, even changing a single bit, immediately invalidates its cryptographic signature. This makes tampering and forgery detectable by default, without relying on visual inspection or backend checks.
### What is selective disclosure? [#what-is-selective-disclosure]
Selective disclosure allows a credential holder to prove a specific fact, such as being over 18, without revealing their exact birthdate or any unrelated attributes. This reduces data exposure and limits correlation across transactions.
### Who controls a verifiable credential? [#who-controls-a-verifiable-credential]
The holder controls their verifiable credentials. They decide when and where to share them, and control exactly what information is disclosed. Verification happens locally, allowing verifiers to confirm authenticity without broadcasting the holder's activity to central systems.
# Developer toolbox
URL: /docs/resources/dev-toolbox
A collection of sample apps, API tools and templates and branding tools that you can use to get
started with MATTR VII quickly and easily.
## Developers [#developers]
* [Postman collection](/docs/api-reference#postman-collection): Preset configuration to get you going with MATTR VII APIs quickly and easily.
* [X.509 certificates validator](https://tools.mattrlabs.com/pem): Decode and validate X.509 certificates.
* [CBOR Decoder](https://tools.mattrlabs.com/cbor): Decode CBOR data into a human readable format.
* [JWT Decoder](https://tools.mattrlabs.com/jwt): Decode JWT data into a human readable format.
## Issuers [#issuers]
* [Pre-authorized Code credential offer generator](https://tools.mattrlabs.com/issue-credential): Generate pre-authorized credential offers for testing.
* [Claims studio](https://www.claims.studio/): Create data stores to be used as claim sources when testing your MATTR VII integrations.
* [Credential templates](https://github.com/mattrglobal/sample-apps/blob/master/credential-templates/README.md): CWT and Semantic CWT credentials template files.
## Holders [#holders]
* [mDoc Online presentation tool](https://tools.mattrlabs.com/mdoc-online-presentation): Test your mDoc presentation capabilities.
## Admins [#admins]
* [Custom Domain checker](https://tools.mattrlabs.com/domain-checker): Check whether your custom domain includes the required redirects.
## DTS Operators [#dts-operators]
* [VICAL viewer](https://tools.mattrlabs.com/vical-viewer): Display a VICAL .cbor file in a human readable format.
Please [reach out](http://mattr.global/contact-us) if you need assistance using any of these
tools.
# Getting started
URL: /docs/resources/get-started
Complete the sign-up form to sign up and trial MATTR capabilities.
# Resources
URL: /docs/resources
Resources provide a wealth of information and tools to help you navigate the landscape of digital trust. In this section, you'll find reference documentation, development toolkits, and legal terms that are essential for building and managing secure, interoperable systems. Whether you're a developer, architect, or business leader, these resources are designed to support you at every stage of your journey.
Sign up to trial MATTR capabilities
API and SDK reference documentation
Tools to improve your development experience
MATTR Changelogs
Supported standards
Terms and conditions
# Reference Documentation
URL: /docs/resources/reference-docs
## MATTR VII [#mattr-vii]
* **MATTR VII Platform API**: The core API for managing and interacting with a MATTR VII tenant.
* [API Reference](/docs/api-reference): Defines a set of capabilities to manage and interact with a MATTR VII tenant.
* [Events Registry](https://api-reference-sdk.mattr.global/event-registry/latest/index.html): A comprehensive list of all events generated by the MATTR VII Platform API.
* **MATTR VII Management API**: Defines a set of capabilities beyond the scope of a single MATTR VII tenant or environment.
* [API Reference](/docs/api-reference/management-api-reference-overview): Defines a set of capabilities beyond the scope of a single MATTR VII tenant or environment.
* [Events Registry](https://api-reference-sdk.mattr.global/event-registry-management/latest/index.html): Includes all events generated by the MATTR VII Management API.
## MATTR Pi [#mattr-pi]
* **React Native SDKs**:
* [mDocs Holder](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/index.html): Build an app for holding, storing and sharing mDocs.
* [mDocs Verifier](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/index.html): Build mDocs verification capabilities into new or existing apps.
* [Holder](https://api-reference-sdk.mattr.global/wallet-sdk-react-native/latest/index.html): Build an app for holding, storing and sharing verifiable credentials.
* [Verifier](https://api-reference-sdk.mattr.global/verifier-sdk-react-native/latest/index.html): Build verification capabilities into new or existing apps.
* **iOS Native SDKs**:
* [mDocs Holder](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk): Build mDocs claiming and presenting capabilities into your native iOS app.
* [mDocs Verifier](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk): Build mDocs verification capabilities into your native iOS app.
* **Android Native SDKs**:
* [mDocs Holder](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/): Build mDocs claiming and presenting capabilities into your native Android app.
* [mDocs Verifier](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/): Build mDocs verification capabilities into your native Android app.
* **Web SDKs**:
* [Verifier Web](https://api-reference-sdk.mattr.global/verifier-sdk-web/latest/index.html): Embed mDocs online verification capabilities into your website.
# Android app signing
URL: /docs/holding/android-app-signing
When registering an Android holder application with your MATTR VII tenant for SDK tethering, you need to provide the `packageSigningCertificateThumbprints` property. This ensures that only your trusted application can interact with the tenant.
The Holder SDK uses this configuration to establish a trusted relationship between your Android app and the MATTR VII platform. Without a valid signing certificate thumbprint, the tenant will reject requests from your application — preventing untrusted or modified apps from claiming credentials or performing other holder operations.
Every Android app must be signed with a certificate before it can be installed. This signing certificate identifies the developer or organization responsible for the app and is used by Android to verify authenticity and integrity.
The package signing certificate thumbprint acts as a unique cryptographic identifier for your app's signing key. By verifying this thumbprint, the MATTR VII tenant can confirm that incoming requests originate from a trusted and unmodified app.
The purpose of this page is to explain how to obtain the signing certificate thumbprint for your Android app.
## What is a Package Signing Certificate Thumbprint? [#what-is-a-package-signing-certificate-thumbprint]
A package signing certificate thumbprint is the hex-encoded SHA-256 hash of your app's signing certificate (the X.509 certificate bytes). It uniquely identifies the certificate used to sign your app and remains the same across app updates as long as the signing certificate does not change.
## Obtaining the Thumbprint [#obtaining-the-thumbprint]
### Production builds [#production-builds]
When your app is ready for release, the thumbprint you configure must match the signing certificate used for your production build.
Obtaining the thumbprint differs based on how you manage your signing keys - via Google Play App Signing or manually.
#### Google Play App Signing [#google-play-app-signing]
If your app uses Play App Signing, Google manages your app's signing key. You can find the SHA-256 fingerprint in the Google Play Console:
1. Go to **Google Play Console** > **Setup** > **App Signing**.
2. Locate the SHA-256 fingerprint under **App Signing Key Certificate**.
3. Copy the SHA-256 value.
4. Remove all `:` characters and convert the string to lowercase.
```js title="Example conversion"
const fingerprint = '91:F7:CB:F9:D6:81:53:1B:C7:A5:8F:B8:33:CC:A1:4D:AB:ED:E5:09:C5:12:34:56:78:9A:BC:DE:F0:12:34:56:78';
// Remove colons and convert to lowercase
const sha256Hex = fingerprint.replaceAll(":", "").toLowerCase();
console.log(sha256Hex)
```
5. Upload the processed value as your `packageSigningCertificateThumbprints` configuration.
For more information, refer to Google's documentation on [Play App Signing Overview](https://developer.android.com/studio/publish/app-signing) and [Manage App Signing Keys in Google Play Console](https://support.google.com/googleplay/android-developer/answer/9842756?hl=en).
#### Manual signing (CLI) [#manual-signing-cli]
1. Retrieve the SHA-256 fingerprint using the `keytool` command for the specific signing key alias:
```bash title="Command to get SHA-256 fingerprint"
keytool -list -v -keystore -alias
```
2. Copy the SHA-256 value.
3. Remove all `:` characters and convert the string to lowercase.
```js title="Example conversion"
const fingerprint = '91:F7:CB:F9:D6:81:53:1B:C7:A5:8F:B8:33:CC:A1:4D:AB:ED:E5:09:C5:12:34:56:78:9A:BC:DE:F0:12:34:56:78:9A:BC:DE:F0:12:34:56:78:9A:BC:DE:F0:12:34:56:78:9A:BC:DE:F0';
const sha256Hex = fingerprint.replaceAll(":", "").toLowerCase();
console.log(sha256Hex)
```
4. Upload the processed value as your `packageSigningCertificateThumbprints` configuration.
### Local builds (Debug) [#local-builds-debug]
1. Extract the signing certificate information directly from the `.apk` file using the apksigner tool:
```bash title="Command to get SHA-256 fingerprint from APK"
apksigner verify --print-certs path/to/your-debug-apk.apk
```
```nginx title="Example output"
Signer #1 certificate DN: C=US, O=Android, CN=Android Debug
Signer #1 certificate SHA-256 digest: f59105881315e61502274a499d6efc2d7cc71c5cae266e598290d36b59221f6d // [!code focus]
Signer #1 certificate SHA-1 digest: ca09773016ef4db66344ce0dac2827429ea875f1
Signer #1 certificate MD5 digest: c59905769e42c09530898c6dc413258f
```
2. Copy the SHA-256 digest and upload it as your `packageSigningCertificateThumbprints` configuration.
The default debug keystore is usually located at: `$HOME/.android/debug.keystore`.
For more details, refer to [Android Debug Signing](https://developer.android.com/studio/publish/app-signing#debug-mode).
## Best practices [#best-practices]
* If your app's signing key changes, you'll need to update your application configuration.
* Consider removing old thumbprints if you wish to **invalidate older app versions** — for example, after a key rotation or potential compromise.
* For development and testing, you can use the debug signing certificate thumbprint, but ensure to switch to the release signing certificate for production builds.
* Always keep your signing keys secure and avoid sharing them publicly.
# Core capabilities to integrate
URL: /docs/holding/core-capabilities
The Holder SDK provides three main capability areas that map to the credential lifecycle. This page
introduces each one and links to the deeper capability overviews and tutorials.
## Core capabilities to integrate [#core-capabilities-to-integrate]
The SDK provides three main capability areas that map to the credential lifecycle.
### Credential claiming [#credential-claiming]
Your app receives credential offers (via QR code scan, deep link, or silent push) and claims them
using the [OID4VCI protocol](/docs/issuance/oid4vci-overview).
**What you implement:**
* Handle incoming credential offer URIs (QR scanner, deep link handler, or push notification).
* Trigger the SDK's claiming flow with the offer URI.
* Display claiming progress and result to the user.
* Store credentials securely (handled by the SDK).
**Key decisions:**
* Which offer channels to support (QR, deep link, push).
* How to surface the claiming experience in your app's navigation.
* Whether to show credential details before accepting.
* Whether your app uses an open model (any compatible wallet can claim) or a restricted model
(only your app can claim via custom URI schemes or App Links/Universal Links). This affects
whether users can scan QR codes with the device's native camera or must use your in-app scanner.
* Whether the issuer requires [wallet attestation](/docs/issuance/credential-issuance/wallet-attestation).
If enabled, the Holder SDK proves your app's authenticity before claiming credentials. The SDK
handles the attestation proofs for you once [SDK Tethering](/docs/holding/sdk-operations/sdk-tethering)
is configured, so no additional wallet backend integration is required.
See [claiming credential offers](/docs/issuance/credential-offer/overview#claiming-credential-offers)
and the [credential claiming overview](/docs/holding/credential-claiming-overview) for details.
Use the [quickstart](/docs/holding/sdk-quickstart) to get started.
### In-person (proximity) presentation [#in-person-proximity-presentation]
Your app presents credentials to nearby verifiers over Bluetooth Low Energy, following
[ISO/IEC 18013-5](https://www.iso.org/standard/69084.html).
**What you implement:**
* Display a QR code for the verifier to scan (engagement phase).
* Handle the BLE connection and data exchange (managed by SDK).
* Show the user what data is being requested and collect consent.
* Display presentation result.
**Key decisions:**
* How to integrate the presentation trigger into your app's UI.
* Consent screen design (what data elements to show, how to explain them).
* How to handle offline scenarios (no network connectivity).
* Whether to prepare for NFC engagement (currently in development). For iOS apps, consider
[applying for the Apple NFC entitlement](https://developer.apple.com/support/nfc-se-platform/)
early to enable NFC-based engagement when available.
See the [proximity presentation overview](/docs/holding/proximity-presentation-overview) and
[tutorial](/docs/holding/proximity-presentation-tutorial).
### Remote presentation [#remote-presentation]
Your app responds to remote verification requests from web or mobile verifiers using
[ISO/IEC 18013-7](https://www.iso.org/standard/91154.html) and
[OID4VP](/docs/verification/oid4vp).
**What you implement:**
* Handle incoming presentation requests (redirect, QR, or Digital Credentials API).
* Show the user what data is being requested and collect consent.
* Submit the presentation response via the SDK.
* Handle same-device and cross-device scenarios.
**Key decisions:**
* Which invocation methods to support (redirects, QR scanning, Digital Credentials API).
* Same-device vs cross-device flow handling.
* How to return the user to the requesting app/website after presentation.
See the [remote presentation overview](/docs/holding/remote-presentation-overview) and
[tutorial](/docs/holding/remote-presentation-tutorial).
## Next steps [#next-steps]
Next, see how these capabilities fit together in the [integration architecture](/docs/holding/integration-architecture).
# Credential claiming
URL: /docs/holding/credential-claiming-overview
## Overview [#overview]
Claiming a credential is the process of receiving a verifiable credential into a digital wallet. From the perspective of a holder (and wallet app developer), this is the core capability that enables individuals to collect and manage their digital credentials, such as identity documents, membership cards, or certifications.
Where issuers are responsible for creating and signing the credential, the holder’s role is to accept, retrieve, and store the credential securely. Claiming ensures the credential can later be presented to verifiers in a way that is trusted, privacy-preserving, and interoperable.
When a user claims a credential, they are:
1. Receiving an offer from an issuer — typically in the form of a QR code, deep link, or push notification.
2. Reviewing details about the issuer and the credential type before deciding to proceed.
3. Authorizing retrieval (through authentication, a pre-authorized code, or other mechanisms depending on the workflow).
4. Requesting and retrieving the credential from the issuer.
5. Storing the credential in their wallet, ready to be presented later to verifiers.
## OID4VCI [#oid4vci]
The [OID4VCI specification](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html)
is an open standard developed by the OpenID Foundation, defining how digital wallets can receive
verifiable credentials from credential issuers in a secure and interoperable way. It leverages the
widely adopted OAuth 2.0 and OpenID Connect to establish a trust framework for credential issuance,
ensuring both privacy and user control.
If you are unfamiliar with OpenID Connect, the identity protocol underpinning
the OpenID provisioning capability, there are many excellent guides available
online such as this guide from
[Google](https://developers.google.com/identity/openid-connect/openid-connect),
or this guide from
[Mozilla](https://infosec.mozilla.org/guidelines/iam/openid_connect.html).
## Workflows [#workflows]
OID4VCI defines two distinct workflows, each tailored to different use cases and requirements:
* [Authorization Code flow](/docs/issuance/authorization-code/overview): This interactive,
user-driven flow requires the credential recipient (typically a wallet) to redirect the user to
the issuer (such as a government or organization) for authentication. After the user successfully
authenticates and gives consent, the issuer's authentication provider returns an authorization
code. The wallet then exchanges this code for an access token, which is used to obtain the
credential.
The following credential formats can be claimed via the Authorization Code flow:
* [CWT](/docs/concepts/cwt)
* [Semantic CWT](/docs/concepts/cwt)
* [mDocs](/docs/concepts/mdocs)
* [Pre-authorized Code flow](/docs/issuance/pre-authorized-code/overview): In this flow, the
issuer prepares the credential issuance in advance and may authenticate and authorize the holder
ahead of time. Instead of obtaining an authorization code through user authentication, the wallet
receives a pre-authorized code directly from the issuer, often via an out-of-band method. The user
does not need to authenticate again and the wallet presents the pre-authorized code to retrieve an
access token and then claim the credential. For added security, the issuer can require a
transaction code (shared separately with the holder) which the wallet must also provide to claim
the credential.
The Pre-authorized Code flow is only supported for [mDocs](/docs/concepts/mdocs).
MATTR VII supports both workflows, allowing you to choose the one that best fits your use case.
# Learn how to build an application that can claim an mDoc via OID4VCI
URL: /docs/holding/credential-claiming-tutorial
Coming soon...
## Introduction [#introduction]
In this tutorial, you will learn how to use the [mDocs Holder SDKs](/docs/holding/sdk-overview)
to build an application that can claim an [mDoc](/docs/concepts/mdocs) issued via both the OID4VCI
[Authorization Code](/docs/issuance/authorization-code/overview) and
[Pre-authorized Code](/docs/issuance/pre-authorized-code/overview) flows.
1. The user launches the application and scans a QR code received from an issuer.
2. The application displays what credential is being offered to the user and by what issuer.
3. The user agrees to claiming the offered credential.
4. The user is redirected to complete authentication (Only in the Authorization Code flow).
5. Upon successful authentication, the credential is issued to the user's application, where they
can now store, view and present it.
The result will look something like this:
## Prerequisites [#prerequisites]
Before you get started, let's make sure you have everything you need.
### Prior knowledge [#prior-knowledge]
* The issuance workflow described in this tutorial is based on the
[OID4VCI](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html)
specification. If you are unfamiliar with this specification, refer to the following resources
for more information:
* What is [credential issuance](/docs/issuance)?
* Breakdown of the [OID4VCI workflow](/docs/issuance/oid4vci-overview).
* Understand the difference between the
[Authorization Code](/docs/issuance/authorization-code/overview) and
[Pre-authorized Code](/docs/issuance/pre-authorized-code/overview) flows.
* What are [mDocs](/docs/concepts/mdocs)?
* We assume you have experience developing applications in the relevant programming languages and
frameworks (Swift for iOS, Kotlin for Android and TypeScript for React Native).
If you need to get a holding solution up and running quickly with minimal
development resources and in-house domain expertise, [talk to
us](http://mattr.global/contact-us) about our white-label [MATTR GO Hold
app](https://mattr.global/platforms/go) which might be a good fit for you.
### Assets [#assets]
* As part of your onboarding process you should have been provided with access to the following
assets ([Contact us](https://mattr.global/contact-us) if you are interested in trialing the SDK):
* ZIP file which includes the required framework:
(`MobileCredentialHolderSDK-*version*.xcframework.zip`).
* Sample Wallet app: You can use this app for reference as you work through this tutorial.
This tutorial is only meant to be used with the most [recent
version](/docs/holding/sdk-overview#versions) of the iOS mDocs Holder SDK.
* As part of your onboarding process you should have been provided with access to the following
assets ([Contact us](https://mattr.global/contact-us) if you are interested in trialing the SDK):
* ZIP file that includes the required library (`mobile-credential-holder-*version*.zip`).
* Sample Wallet app: You can use this app for reference as you work through this tutorial.
This tutorial is only meant to be used with the most [recent
version](/docs/holding/sdk-overview#versions) of the Android mDocs Holder SDK.
* You will need access to the SDK and additional MATTR dependencies to complete this tutorial.
[Contact us](https://mattr.global/contact-us) if you are interested in trialing the SDK.
This tutorial is only meant to be used with the most [recent
version](/docs/holding/sdk-overview#versions) of the React Native mDocs Holder
SDK.
### Development environment [#development-environment]
* [Xcode](https://developer.apple.com/xcode/) setup with either:
* Local build settings if you are developing locally.
* [iOS developer account](https://developer.apple.com/programs/enroll/) if you intend to publish
your app.
* [Android Studio](https://developer.android.com/studio/).
* Code editor (such as [VS Code](https://code.visualstudio.com/download)).
* [Android Studio](https://developer.android.com/studio/).
* [Xcode](https://developer.apple.com/xcode/).
* [yarn](https://yarnpkg.com/) (v1.22.22 was used during development).
* Java v17.
This tutorial uses [Expo Go](https://expo.dev/go), leveraging [Development
Builds](https://docs.expo.dev/develop/development-builds/introduction/).
**Dependencies**
All dependencies are included in the project's `package.json` file and are automatically installed
as part of the [environment setup](#install-the-dependencies).
`@mattrglobal/mobile-credential-holder-react-native`
**Additional core and utility dependencies:**
* `expo-build-properties`
* `expo-router` (including `react-native-safe-area-context` `react-native-screens` `expo-linking`
`expo-constants` `expo-status-bar`) - used for file-based routing.
* `react-native-vision-camera` - handles camera permissions and QR code scanning for interacting
with QR code credential offers.
* `expo-web-browser` - used to open the authorization URL.
* `react-native-qrcode-svg` - used for the future proximity presentation tutorial.
### Testing device [#testing-device]
* Supported iOS device to run the built application on, setup with:
* Biometric authentication (Face ID, Touch ID).
* Available internet connection.
* Supported Android device to run the built application on, setup with:
* Biometric authentication (Face recognition, fingerprint recognition).
* Available internet connection.
* [Debugging](https://developer.android.com/studio/debug/dev-options#enable) enabled.
* Supported iOS and/or Android device to run the built application on, setup with:
* Available internet connection.
* iOS:
* Biometric authentication (Face ID, Touch ID).
* Android:
* Biometric authentication (Face recognition, fingerprint recognition).
* [Debugging](https://developer.android.com/studio/debug/dev-options#enable) enabled.
Got everything? Let's get going!
## Environment setup [#environment-setup]
Perform the following steps to setup and configure your development environment:
**Step 1: Create a new project**
Please follow the detailed instructions to
[Create a new Xcode project](https://help.apple.com/xcode/mac/current/#/dev07db0e578) and add your
organization’s identifier.
**Step 2: Unzip the dependencies file**
1. Unzip the `MobileCredentialHolderSDK-*version*.xcframework.zip` file.
2. Drag the `MobileCredentialHolderSDK-*version*.xcframework` folder into your project.
3. Configure `MobileCredentialHolderSDK.xcframework` to
[Embed & sign](https://help.apple.com/xcode/mac/current/#/dev51a648b07).
See [Add existing files and folders](https://help.apple.com/xcode/mac/current/#/dev81ce1d383) for
detailed instructions.
This should result in the the following framework being added to your project:
**Step 3: Configure required resources**
1. [Create a new file](https://developer.apple.com/documentation/xcode/managing-files-and-folders-in-your-xcode-project)
named `Constants.swift` within your project.
2. [Add the following string resources](https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package)
to represent the Authentication provider you will use for this tutorial:
```swift title="Constants.swift"
enum Constants {
static let redirectUri: String = "io.mattrlabs.sample.mobilecredentialholderapp://credentials/callback"
static let clientId: String = "ios-sample-mobile-credential-holder-app"
}
```
* `redirectUri` : This is the path the SDK will redirect to once the user completes
Authentication with the issuer. Our best practice recommendation is to configure this to be
`{redirect.scheme}://credentials/callback` as shown in the example above. However, it can be
any path as long as it is handled by your application and registered with the issuer against
the corresponding `clientId`.
* `clientId` : This is the identifier that is used by the issuer to recognize the wallet
application. This is only used internally in the interaction between the wallet and the issuer
and can be any string as long as it is registered with the issuer as a trusted wallet
application.
Both of these parameters are only used in the Authorization Code flow and must
be registered as a key pair as part of the **issuer's** OID4VCI workflow
configuration. In this tutorial you will be claiming a credential from a MATTR
Labs issuer which is configured with the parameters detailed above. We will
help you configure your unique values as you move your implementation into
production.
**Step 4: Add Bluetooth and biometric permissions**
The SDK requires access to the mobile device Bluetooth and biometric capabilities for the different
workflows built in this tutorial.
[Configure these permissions](https://help.apple.com/xcode/mac/current/#/dev37c2f42ff) in the `Info`
tab of the Application target:
**Step 5: Run the application**
Select **Run** and make sure the application launches with a *“Hello, world!”* text in the middle of
the display, as shown in the following image:
**Step 1: Create a new project**
1. [Create a new Android Studio project](https://developer.android.com/studio/projects/create-project),
using the *Empty Activity* template.
2. Name the project `Holder Tutorial`.
3. Select *API 24* as the `Minimum SDK` version.
4. Select *Kotlin DSL* as the `Build configuration language`.
5. Select **Finish**.
6. [Sync](https://developer.android.com/build#sync-files) the project with Gradle files.
**Step 2: Add required dependencies**
1. Select the [Project view](https://developer.android.com/studio/projects#ProjectView).
2. Create a new directory named `repo` in your project's folder.
3. Unzip the `mobile-credential-holder-*version*.zip` file and copy the unzipped `global`
folder into the new `repo` folder.
4. Open the `settings.gradle.kts` file in the `HolderTutorial` folder and add the following Maven
repository to the `dependencyResolutionManagement.repositories` block:
```kotlin title="settings.gradle.kts"
maven {
url = uri("repo")
}
```
5. Open the `app/build.gradle.kts` file in your `app` folder and add the following dependencies to
the `dependencies` block:
```kotlin title="app/build.gradle.kts"
implementation("global.mattr.mobilecredential:holder:7.0.0")
implementation("androidx.navigation:navigation-compose:2.9.0")
```
* The `holder` dependency version should match the version of the unzipped
`mobile-credential-holder-*version*.zip` file you copied to the `repo` folder.
* The required `navigation-compose` version may differ based on your version of the IDE, Gradle, and other project dependencies.
6. In your app's `AndroidManifest.xml` file, add the following `activity` declaration. This
represents the [Authentication provider](/docs/issuance/authorization-code/authentication-provider/overview) you will
use for this tutorial:
```xml title="AndroidManifest.xml"
```
These values are part of the `redirect URI` the SDK will redirect the user to once they complete
Authentication with the issuer:
* `host` : The `host` can be any path, but our best practice recommendation is to configure this
to be `credentials`, as the standard format for the `redirect URI` is
`{redirect.scheme}://credentials/callback`.
* `scheme` : The `scheme` can be any path that is handled by your application and registered
with the issuer.
These values (alongside the client ID, which will be discussed later) are only
used in the Authorization Code flow and must be registered as part of the
**issuer's** OID4VCI workflow `redirect URI`. In this tutorial you will be
claiming a credential from a MATTR Labs issuer which is already configured
with the parameters detailed above. We will help you configure your unique
values as you move your implementation into production.
7. [Sync](https://developer.android.com/build#sync-files) the project with Gradle files.
8. Open the [Build](https://developer.android.com/studio/run#gradle-console) tab and select `Sync`
to make sure that the project has synced successfully.
**Step 3: Run the application**
1. Connect a [debuggable](https://developer.android.com/studio/debug/dev-options#Enable-debugging)
Android mobile device to your machine.
2. [Build and run the app](https://developer.android.com/studio/run) on the connected mobile
device.
The app should launch with a *“Hello, Android!”* text displayed.
**Step 1: Access the tutorial codebase**
1. Access the tutorial starter codebase by either:
* Cloning the MATTR sample apps repository:
```bash title="Clone the repository"
git clone https://github.com/mattrglobal/sample-apps.git
```
or
* Downloading the starter directory using the
[download-directory.github.io](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2Fmattrglobal%2Fsample-apps%2Ftree%2Fmaster%2Freact-native-mdocs-holder-tutorial%2Freact-native-mdocs-holder-tutorial-starter)
utility.
2. Open the tutorial project in your code editor. You can find it in the sample-apps/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-starter/ directory.
When cloning the entire sample apps repository, you can find the completed
tutorial code in the
`sample-apps/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-complete`
directory and use it as a reference.
**Step 2: iOS Application configuration**
1. Open the `app.config.ts` file and update the `bundleIdentifier` value under the
`// Update the bundle identifier` comment to a unique value for your application, e.g.
`com.mycompany.myapp`.
```typescript title="app.config.ts"
bundleIdentifier: "com.mycompany.myapp",
```
iOS requires each app to have a unique bundle identifier for App Store and
development environments.
2. Add the following Face ID (`NSFaceIDUsageDescription`) and camera usage
(`NSCameraUsageDescription`) permissions to the `ios.infoPlist` object under the
`// Add Face ID and Camera usage permissions` comment:
```ts title="app.config.ts"
NSFaceIDUsageDescription: "Face ID is used to secure your credentials.",
NSCameraUsageDescription: "Camera is used to scan QR codes.",
```
These permissions are required for the SDK to use biometric authentication and the camera for QR
code scanning.
3. Add the following code under the `// Add Bluetooth permissions.` comment in the `app.config.ts`
file to add Bluetooth permissions to the application:
```ts title="app.config.ts"
NSBluetoothAlwaysUsageDescription: "This app uses Bluetooth to communicate with verifiers or holders.",
NSBluetoothPeripheralUsageDescription: "This app uses Bluetooth to communicate with verifiers or holders.",
```
These permissions will be required for the SDK to use Bluetooth for device engagement and
communication in the
[proximity presentation tutorial](/docs/holding/proximity-presentation-tutorial).
**Step 3: Configure the SDK plugins**
Add the following code under the `// Configure the SDK plugins` comment to import required plugin
configurations:
```ts title="app.config.ts"
"./withAndroidHolderSDK.js",
"./withRemoveAPNSCapability.js",
```
The SDK requires platform-specific configurations to work correctly. The plugin files have already
been created in your project root directory. You can also follow the instructions in the
[mDocs Holder](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/index.html#md:install-dependencies)
SDK Docs to perform these platform-specific configuration manually.
**Step 4: Install the dependencies**
1. Open a terminal in the project's root and navigate to the starter project directory:
```bash title="Navigate to the project starter directory"
cd sample-apps/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-starter/
```
2. Install the application [dependencies](#dependencies):
```bash title="Install dependencies"
yarn install
```
**Step 5: Generate the iOS and Android project files**
Run the following command to generate the iOS and Android project files:
```bash title="Generate project files"
yarn expo prebuild
```
You should now see the `ios` and `android` folders in your project root.
**Step 6: Start the application**
Connect your testing device(s) and run the following command to start the application(s):
**iOS**
```bash title="Start iOS application"
yarn ios --device
```
**Android**
```bash title="Start Android application"
yarn android --device
```
Nice work, your application is now all set to begin using the SDK!
## Tutorial steps [#tutorial-steps]
In this part of the tutorial you will build the capabilities for the user to interact with an
OID4VCI [credential offer](/docs/issuance/credential-offer/overview) and claim an
[mDoc](/docs/concepts/mdocs).
To achieve this, you will break this capability down into the following steps:
1. Initialize the SDK.
2. Interact with a Credential offer.
3. Retrieve offer details and present them to the Holder.
4. Obtain user consent and initiate Credential issuance.
### Step 1: Initialize the SDK [#step-1-initialize-the-sdk]
The first capability you will build into your app is to initialize the SDK so that your app can use
SDK methods and classes. To achieve this, you need to import the `MobilecredentialHolderSDK`
framework and then initialize the `MobileCredentialHolder` class:
1. Open the `ContentView` class in your new app project and replace any existing code with the
following:
```swift title="ContentView"
import SwiftUI
// Claim Credential - Step 1.2: Import MobileCredentialHolderSDK
struct ContentView: View {
@State var viewModel: ViewModel = ViewModel()
var body: some View {
NavigationStack(path: $viewModel.navigationPath) {
VStack {
Button("Claim Credential") {
viewModel.navigationPath.append(NavigationState.qrScan)
}
.padding()
createQRCodeButton
if viewModel.shouldDisplayOnlinePresentation {
Button("View Online Presentation Session") {
viewModel.navigationPath.append(NavigationState.onlinePresentation)
}
.padding()
}
Spacer()
}
.padding()
.navigationDestination(for: NavigationState.self) { destination in
switch destination {
case .qrScan:
codeScannerView
case .credentialOffer:
credentialOfferView
case .transactionCodeInput:
transactionCodeInputView
case .retrievedCredentials:
retrievedCredentialsView
case .onlinePresentation:
// Online Presentation - Step 3.3: Display online presentation view
EmptyView()
case .presentCredentials:
qrCodeView
case .proximityPresentation:
// Proximity Presentation - Step 2.5: Display proximity presentation view
EmptyView()
}
}
// Online Presentation - Step 2.4: Create session from request URI
}
// Claim Credential - Step 1.4: Initialize the SDK when the view appears
.task {
await viewModel.initialize()
}
}
// MARK: - Credential Retrieval Views
var codeScannerView: some View {
// Claim Credential - Step 2.4 Create QRScannerView
EmptyView()
}
var credentialOfferView: some View {
// Claim Credential - Step 3.5: Display Credential offer
EmptyView()
}
var transactionCodeInputView: some View {
// Claim Credential - Step: 3.4 Display transaction code input view.
EmptyView()
}
var retrievedCredentialsView: some View {
// Claim Credential - Step 4.4: Display retrieved credentials
EmptyView()
}
// MARK: - Proximity Presentation Views
var createQRCodeButton: some View {
// Proximity Presentation - Step 1.5: Add button to generate QR code
EmptyView()
}
func generateQRCode(data: Data) -> Data? {
// Proximity Presentation - Step 1.6: Generate QR code
return nil
}
var qrCodeView: some View {
// Proximity Presentation - Step 1.7: Create QR code view
EmptyView()
}
}
@Observable class ViewModel {
var navigationPath = NavigationPath()
// Claim Credential - Step 1.3: Add MobileCredentialHolder var
// Claim Credential - Step 3.1: Add DiscoveredCredentialOffer and discoveredCredentialOfferURL vars
// Claim Credential - Step 4.1: Add retrievedCredentials var
// Proximity Presentation - Step 1.2: Create deviceEngagementString and proximityPresentationSession variables
// Proximity and Online Presentation: Create variables for credential presentations
// Online Presentation - Step 2.1: Create a variable to hold the online presentation session object
var shouldDisplayOnlinePresentation: Bool {
// Online Presentation - Step 3.4: View Online Presentation
return false
}
// Claim Credential - Step 1.4: Initialize MobileCredentialHolder SDK
@MainActor
func getCredential(id: String) {
// Proximity and Online Presentation: Retrieve a credential from storage
print("This method will get a credential from storage and update the viewModel")
}
}
// MARK: - Credential Retrieval
extension ViewModel {
@MainActor
func discoverCredentialOffer(_ offer: String) {
// Claim Credential - Step 3.2: Add discover credential offer logic
print("This method will discover a credentials offer and update viewModel")
}
@MainActor
func retrieveCredential(transactionCode: String?) {
// Claim Credential - Step 4.2: Call retrieveCredential method
print("This method will save a credential from offer and store it in the application's storage ")
}
}
// MARK: - Online Presentation
extension ViewModel {
@MainActor
func createOnlinePresentationSession(authorizationRequestURI: String) async {
// Online Presentation - Step 2.3: Create online presentation session
print("This method will create an online presentation session and update viewModel")
}
@MainActor
func sendOnlinePresentationSessionResponse(id: String) {
// Online Presentation - Step 4.1: Send online presentation response
print("This method will be passed to a view and send a response with selected credentials")
}
}
// MARK: - Proximity Presentation
extension ViewModel {
func createDeviceEngagementString() {
// Proximity Presentation - Step 1.3: Create function to create a proximity presentation session and generate QR code
print("This method will create a device engagement string that will be converted to a QR code")
}
// Proximity Presentation - Step 1.4: Update function signature
func onRequestReceived() {
// Proximity Presentation - Step 2.2: Store credential requests and matched credentials
print("The signature of this method will need to be updated to include the correct parameters")
print("This is a method that will be called when a proximity presentation request is received")
}
@MainActor
func sendProximityPresentationResponse(id: String) {
// Proximity Presentation - Step 3.1: Send a credential response
print("This method will be passed to a view and send a response with selected credentials")
}
}
// MARK: - Navigation
enum NavigationState: Hashable {
case qrScan
case credentialOffer
case transactionCodeInput
case retrievedCredentials
case onlinePresentation
case presentCredentials
case proximityPresentation
}
```
This will serve as the basic structure for your application for this and the future tutorials.
We will copy and paste different
code snippets into specific locations to achieve the different functionalities.
These locations are indicated by comments that reference both the section and the step (e.g.
`// Claim Credential - Step 1.2: Import MobileCredentialHolderSDK`).
We recommend copying and pasting the comment text to easily locate it in the code.
2. Add the following code after the
`// Claim Credential - Step 1.2: Import MobileCredentialHolderSDK` comment to import
`MobileCredentialHolderSDK` and enable using its capabilities in your application:
```swift title="ContentView"
import MobileCredentialHolderSDK
```
3. Add the following code after the
`// Claim Credential - Step 1.3: Add MobileCredentialHolder var` comment to create a variable
that holds the `mobileCredentialHolder` instance:
```swift title="ContentView"
var mobileCredentialHolder: MobileCredentialHolder
```
4. Add the following code after the
`// Claim Credential - Step 1.4: Initialize MobileCredentialHolder SDK` comment. As of iOS Holder
SDK v6.0.0, `initialize` is asynchronous, so we keep the `init()` synchronous (assigning the
shared instance only) and perform initialization in an `async` method. We call this method from a
[`.task`](https://developer.apple.com/documentation/swiftui/view/task\(id:name:executorpreference:priority:file:line:_:\)) modifier on
the view in the next step, so it runs once when the view appears:
```swift title="ContentView"
init() {
mobileCredentialHolder = MobileCredentialHolder.shared
}
@MainActor
func initialize() async {
do {
try await mobileCredentialHolder.initialize(
userAuthenticationConfiguration: UserAuthenticationConfiguration(userAuthenticationBehavior: .onDeviceKeyAccess),
credentialIssuanceConfiguration: CredentialIssuanceConfiguration(
redirectUri: Constants.redirectUri,
autoTrustMobileCredentialIaca: true
)
)
} catch {
print(error)
}
}
```
Let's review the parameters that are passed into `initialize`:
* `userAuthenticationConfiguration`: Defines when is user authentication required. In this example,
authentication will only be required when a credential is issued and/or presented. Refer to the
[SDK Docs](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/userauthenticationbehavior)
to see all options.
* `CredentialIssuanceConfiguration` :
* `redirectUri`: This is the URI that the SDK uses to redirect the user back to your wallet
application after authentication is complete. It must match the value you configured earlier
in the [development environment setup](#configure-required-resources). This value is only used
and required in the Authorization Code flow.
* `autoTrustMobileCredentialIaca`: Controls how the SDK handles issuer IACA certificates during
credential issuance.
* If set to `true`, the SDK will automatically download and trust the issuer’s IACA
certificate(s) when claiming a credential. This allows credentials to be claimed from any
issuer.
* If set to `false`, the SDK will only accept credentials from issuers whose IACA
certificates have already been manually added to the SDK’s trusted issuers list (see
[addTrustedIssuerCertificates](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/addtrustedissuercertificates\(certificates:\))).
This requires the application to manually manage IACA certificates.
5. [Run](https://developer.apple.com/documentation/xcode/running-your-app-in-simulator-or-on-a-device)
the app to make sure it compiles properly.
The first capability you will build into your app is to initialize the SDK so that your app can use
SDK functions and classes. To achieve this, you need to initialize the `MobileCredentialHolder`
class:
1. Open the `MainActivity` file in your project and replace any existing code with the following:
```kotlin title="MainActivity.kt"
package com.example.holdertutorial
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navDeepLink
import com.example.holdertutorial.ui.theme.HolderTutorialTheme
import global.mattr.mobilecredential.holder.dto.MobileCredential
import global.mattr.mobilecredential.holder.MobileCredentialHolder
import global.mattr.mobilecredential.holder.ProximityPresentationSession
import global.mattr.mobilecredential.holder.issuance.CredentialIssuanceConfiguration
import global.mattr.mobilecredential.holder.issuance.dto.DiscoveredCredentialOffer
import global.mattr.mobilecredential.holder.issuance.dto.RetrieveCredentialResult
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
// Claim Credential - Step 1.2: Initialize the SDK
setContent {
HolderTutorialTheme {
val navController = rememberNavController()
Scaffold { innerPadding ->
NavHost(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
.padding(8.dp),
startDestination = "home",
navController = navController,
) {
composable("home") {
HomeScreen(this@MainActivity, navController)
}
composable("scanOffer") {
// Claim Credential - Step 2.5: Add "Scan Offer" screen call
}
composable("retrievedCredential") {
// Claim Credential - Step 4.9: Add "Retrieved Credential" screen call
}
composable("presentationQr") {
// Proximity Presentation - Step 1.2: Add "QR Presentation" screen call
}
composable("presentationSelectCredentials") {
// Proximity Presentation - Step 2.6: Add "Select Credential" screen call
}
// Online Presentation - Step 2.2: Add "Online Presentation" screen call
}
}
}
}
}
}
@Composable
fun HomeScreen(activity: Activity, navController: NavController) {
val coroutineScope = rememberCoroutineScope()
var transactionCode by remember { mutableStateOf("") }
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Button(onClick = { navController.navigate("scanOffer") }, Modifier.fillMaxWidth()) {
Text("Claim Credential")
}
// Proximity Presentation - Step 1.3: Add button for starting the credentials presentation workflow
// Claim Credential - Step 3.3: Display discovered credential offer
}
}
// Claim Credential - Step 4.4: Create function to retrieve credentials
object SharedData {
// Claim Credential - Step 3.1: Add discovered credential offer variables
// Claim Credential - Step 4.2: Add retrieved credentials variable
// Proximity Presentation - Step 2.1: Add proximity presentation request variable
}
```
This will serve as the basic structure for your application. We will copy and paste different
code snippets into specific locations in this codebase to achieve the different functionalities.
These locations are indicated by comments that reference both the section and the step.
We recommend leaving the comment text (e.g. `// Claim Credential - Step 1.2: Initialize the SDK`)
even after you have pasted the code snippet, as it will later help you to easily locate the
step in the code.
2. Add the following code after the `// Claim Credential - Step 1.2: Initialize the SDK` comment to
initialize the SDK by creating a new instance of the `MobileCredentialHolder` class:
```kotlin title="MainActivity.kt"
lifecycleScope.launch {
try {
MobileCredentialHolder.getInstance().initialize(
context = this@MainActivity,
// Step 4.1: Add credential issuance configuration
)
} catch (e: Exception) {
Log.e("MainActivity", "SDK initialization failed", e)
}
}
```
This will initialize the SDK, making it available for your application.
3. [Run](https://developer.android.com/studio/run#basic-build-run) the app to make sure it compiles
properly.
The first capability you will build is to initialize the SDK so that your app can use its methods
and classes. To achieve this you will create a React Context that will allow accessing an SDK
instance and its helper functions throughout the application.
1. In your project's `providers` directory, create a new file named `HolderProvider.tsx` and add the
following scaffolding code:
```ts title="/providers/HolderProvider.tsx"
import {
type MobileCredentialMetadata,
UserAuthenticationBehavior,
UserAuthenticationType,
deleteCredential,
getCredentials,
initialize,
isInitialized,
} from "@mattrglobal/mobile-credential-holder-react-native";
// Online Presentation - Step 2.3: Import expo-linking and expo-router
import type React from "react";
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
import { Alert } from "react-native";
type HolderContextProps = {
isHolderInitialized: boolean;
getMobileCredentials: () => Promise;
mobileCredentials: MobileCredentialMetadata[];
deleteMobileCredential: (credentialId: string) => Promise;
error: string | null;
isLoading: boolean;
};
const HolderContext = createContext(
undefined,
);
export function HolderProvider({ children }: { children: React.ReactNode }) {
const [isHolderInitialized, setIsHolderInitialized] =
useState(false);
const [mobileCredentials, setMobileCredentials] = useState<
MobileCredentialMetadata[]
>([]);
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(true);
// Online Presentation - Step 2.4: Initialize router variable
// Claim a Credential - Step 1.2: Initialize the Holder SDK
const getMobileCredentials = useCallback(async () => {
if (!isHolderInitialized) return;
const credentials = await getCredentials();
setMobileCredentials(credentials);
}, [isHolderInitialized]);
// When the holder is initialized, get the mobile credentials to display in the app
useEffect(() => {
if (isHolderInitialized) {
getMobileCredentials();
}
}, [isHolderInitialized, getMobileCredentials]);
// An example implementation of deleting a credential, used for demonstration purposes
const deleteMobileCredential = useCallback(
async (credentialId: string) => {
if (!isHolderInitialized) return;
Alert.alert(
"Confirm Deletion",
"Are you sure you want to delete this credential?",
[
{ text: "Cancel", style: "cancel" },
{
text: "Delete",
style: "destructive",
onPress: async () => {
try {
await deleteCredential(credentialId);
Alert.alert("Success", "Credential deleted successfully.");
await getMobileCredentials();
} catch (err) {
console.error("Error deleting credential:", err);
Alert.alert(
"Error",
err instanceof Error
? err.message
: "Failed to delete credential.",
);
}
},
},
],
);
},
[isHolderInitialized, getMobileCredentials],
);
// Online Presentation - Step 2.5: Handle deep link
const contextValue = useMemo(
() => ({
isHolderInitialized,
error,
isLoading,
getMobileCredentials,
mobileCredentials,
deleteMobileCredential,
}),
[
isHolderInitialized,
error,
isLoading,
getMobileCredentials,
mobileCredentials,
deleteMobileCredential,
],
);
return (
{children}
);
}
export function useHolder() {
const context = useContext(HolderContext);
if (context === undefined) {
throw new Error("useHolder must be used within a HolderProvider");
}
return context;
}
```
This will serve as the basic structure for your application. You will copy and paste different code
snippets into specific locations in this codebase to achieve the different functionalities. These
locations are indicated by comments that reference both the section and the step number.
We recommend copying and pasting the comment's text (e.g.
`// Claim a Credential - Step 1.2: Initialize the Holder`) to easily locate it in the code.
2. Add the following code under the `// Claim a Credential - Step 1.2: Initialize the Holder SDK`
comment to call the SDK's
[`initialize`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/initialize.html)
method and initialize the SDK:
```ts title="/providers/HolderProvider.tsx"
const initializeHolder = useCallback(async () => {
try {
// Check if SDK is already initialized
const alreadyInitialized = await isInitialized();
if (alreadyInitialized) {
setIsHolderInitialized(true);
return;
}
// Initialize the SDK with authentication and credential issuance configuration
const result = await initialize({
userAuthenticationConfiguration: {
userAuthenticationBehavior: UserAuthenticationBehavior.OnInitialize,
userAuthenticationType: UserAuthenticationType.BiometricOrPasscode,
},
credentialIssuanceConfiguration: {
autoTrustMobileCredentialIaca: true,
redirectUri: "io.mattrlabs.sample.reactnativemobilecredentialholdertutorialapp://credentials/callback"
}
});
if (result.isErr()) {
setError(result.error.message || "Failed to initialize holder.");
} else {
setIsHolderInitialized(true);
}
} catch (err) {
setError(
err instanceof Error
? err.message
: "Unknown error during initialization"
);
} finally {
setIsLoading(false);
}
}, []);
useEffect(() => {
initializeHolder();
}, [initializeHolder]);
```
3. Open the `/app/_layout.tsx` file and replace its content with the following code to wrap the
application with the `HolderProvider` context:
```ts title="/app/_layout.tsx"
import { HolderProvider } from "@/providers/HolderProvider";
import { Stack } from "expo-router";
export default function RootLayout() {
return (
// Claim a Credential - Step 1.3: Wrap the app in the HolderProvider component to make the HolderContext available to any child components
);
}
```
Your application can now use the SDK's methods and classes. Next, you will build the capability to
interact with a Credential offer.
The application will now require biometric authentication whenever the SDK is
initialized.
### Step 2: Interact with a Credential offer [#step-2-interact-with-a-credential-offer]
Users can receive OID4VCI Credential offers as deep-links or QR codes. In this tutorial you will use
a MATTR Labs OID4VCI [Credential offer](/docs/issuance/credential-offer/overview) rendered as a QR
code.
Creating your own Credential offer is not within the scope of the current
tutorial. You can follow the [OID4VCI
guide](/docs/issuance/credential-offer/guide) that will walk you through
creating one.
Your application needs to let users interact with Credential offers. Since this tutorial uses a QR
code to deliver the offer, your application must be able to scan and process QR codes.
For ease of implementation, you will use a third party framework to achieve this:
1. Add [camera usage permissions](https://help.apple.com/xcode/mac/current/#/dev37c2f42ff) to the
app target:
2. Add the [CodeScanner](https://github.com/twostraws/CodeScanner) library via
[Swift Package Manager](https://help.apple.com/xcode/mac/current/#/devb83d64851).
3. [Create a new swift file](https://help.apple.com/xcode/mac/current/#/dev81ce1d383) named
`QRScannerView` and add the following code into it to implement the QR scanning capability:
```swift title="QRScannerView"
import SwiftUI
import CodeScanner
import AVFoundation
struct QRScannerView: View {
private let completionHandler: (String) -> Void
init(completion: @escaping (String) -> Void) {
completionHandler = completion
}
var body: some View {
CodeScannerView(codeTypes: [.qr]) { result in
switch result {
case .failure(let error):
print(error.localizedDescription)
case .success(let result):
print(result.string)
completionHandler(result.string)
}
}
}
}
```
4. Return to the `ContentView` file and replace the `EmptyView()` under the
`// Claim Credential - Step 2.4 Create QRScannerView` comment with the following code to create a
new `QRScannerView` view in the application for scanning QR codes:
```swift title="ContentView"
QRScannerView(
completion: { credentialOffer in
viewModel.discoverCredentialOffer(credentialOffer)
}
)
```
5. [Run](https://developer.apple.com/documentation/xcode/running-your-app-in-simulator-or-on-a-device)
the app and tap the **Claim Credential** button. When prompted, grant camera access to allow
QR code scanning.
You should see a result similar to the following:
As the user selects the **Claim Credential** button, the app launches the device camera to
enable the user to scan a QR code.
You might notice that nothing happens after scanning a QR code - this is expected. In the next step
you will implement the logic that retrieves the credential offer details from the QR code and
presents them to the user.
For ease of implementation, you will use a third party library to achieve this:
1. Add the following dependencies to your `app/build.gradle.kts` file:
```kotlin title="app/build.gradle.kts"
implementation("com.google.accompanist:accompanist-permissions:0.36.0")
implementation("com.journeyapps:zxing-android-embedded:4.3.0")
```
2. [Sync](https://developer.android.com/build#sync-files) your project with Gradle files.
3. In your package, create a new file named `ScanOfferScreen.kt`.
4. Add the following code to the new file:
```kotlin title="ScanOfferScreen.kt"
package com.example.holdertutorial
import android.Manifest
import android.app.Activity
import android.content.Context
import android.util.Log
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import androidx.navigation.NavController
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import com.journeyapps.barcodescanner.BarcodeCallback
import com.journeyapps.barcodescanner.DecoratedBarcodeView
import global.mattr.mobilecredential.holder.MobileCredentialHolder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
// Gets the permissions and shows the screen content, when the permissions are obtained
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun ScanOfferScreen(navController: NavController) {
val cameraPermissionState = rememberPermissionState(Manifest.permission.CAMERA)
val requestPermissionLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) {}
LaunchedEffect(cameraPermissionState) {
if (!cameraPermissionState.status.isGranted) {
requestPermissionLauncher.launch(Manifest.permission.CAMERA)
}
}
if (cameraPermissionState.status.isGranted) Content(navController)
}
// Screen content
@Composable
private fun Content(navController: NavController) {
val context = LocalContext.current
val barcodeView = remember { DecoratedBarcodeView(context) }
val coroutineScope = rememberCoroutineScope()
var isQrScanned by remember { mutableStateOf(false) }
val barcodeCallback = remember {
BarcodeCallback { result ->
// Executed when the QR code was scanned
coroutineScope.launch { onQrScanned(context, result.text, navController) }
barcodeView.pause()
isQrScanned = true
}
}
// Setting up the QR scanner
DisposableEffect(Unit) {
barcodeView.decodeContinuous(barcodeCallback)
barcodeView.resume()
onDispose { barcodeView.pause() }
}
// Showing the scanner until the QR is scanned. Showing a progress bar after that
if (!isQrScanned) {
AndroidView(factory = { barcodeView }, modifier = Modifier.fillMaxSize())
} else {
Box(Modifier.fillMaxSize()) {
CircularProgressIndicator(Modifier.align(Alignment.Center))
}
}
}
private suspend fun onQrScanned(context: Context, offer: String, navController: NavController) {
// Step 3.2: Discover credential offer
}
```
5. Back in the `MainActivity` file, add the following code under the
`// Claim Credential - Step 2.5: Add Scan Offer screen call` comment to connect the created
composable to the navigation graph:
```kotlin title="MainActivity.kt"
ScanOfferScreen(navController)
```
6. [Run](https://developer.android.com/studio/run#basic-build-run) the app and select the **Scan
Credential Offer** button.
As the user selects the **Claim Credential** button, the app asks for camera permission, and
launches the device camera to enable the user to scan a QR code.
1. Replace the code in `/app/index.tsx` with the following:
```ts title="/app/index.tsx"
// The Index component displays the list of credentials and enables claiming new credentials using a QR code scanner.
import { useRouter } from "expo-router";
import React, { useState } from "react";
import {
ActivityIndicator,
Modal,
StyleSheet,
Text,
TouchableOpacity,
View,
} from "react-native";
import CredentialsList from "@/components/CredentialsList";
// Claim a Credential - Step 2.3: Import the QRCodeScanner component
import { useHolder } from "@/providers/HolderProvider";
export default function Index() {
const router = useRouter();
const {
isHolderInitialized,
error,
isLoading,
mobileCredentials,
deleteMobileCredential,
} = useHolder();
const [isScannerVisible, setIsScannerVisible] = useState(false);
// Claim a Credential - Step 2.4: Define the handleScanComplete function
if (isLoading) {
return (
Loading...
);
}
if (error) {
return (
Error: {error}Restart the app.
);
}
if (!isHolderInitialized) {
return (
No holder instance
);
}
return (
Welcome to the mDoc Holder App
{/* UI to enable the QR code scanner */}
setIsScannerVisible(true)}
>
Claim Credential
{/* Proximity Presentation - Step 1.5: Add Proximity Presentation button */}
{/* Online Presentation - Step 2.7: Add Online Presentation button */}
{/* Display the list of credentials and their metadata */}
{/* Modal to display the QR code scanner */}
{/* Claim a Credential - Step 2.5: Add the QRCodeScanner component */}
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 16,
paddingHorizontal: 16,
},
centered: {
flex: 1,
alignItems: "center",
justifyContent: "center",
},
headerText: {
fontSize: 20,
fontWeight: "600",
marginBottom: 20,
},
buttonContainer: {
marginBottom: 20,
gap: 10,
},
button: {
backgroundColor: "#007AFF",
paddingVertical: 12,
paddingHorizontal: 20,
borderRadius: 8,
alignItems: "center",
justifyContent: "center",
},
buttonText: {
color: "white",
fontSize: 16,
fontWeight: "600",
},
closeButton: {
marginBottom: 20,
backgroundColor: "#FF3B30",
},
modalContainer: {
flex: 1,
justifyContent: "center",
},
});
```
2. In your project's `components` directory, create a new file named `QRCodeScanner.tsx` and add the
following code:
```tsx title="/components/QRCodeScanner.tsx"
import React, { useRef } from "react";
import {
Dimensions,
StyleSheet,
Text,
TouchableOpacity,
View,
} from "react-native";
import {
Camera,
useCameraDevice,
useCameraPermission,
useCodeScanner,
} from "react-native-vision-camera";
type QRCodeScannerProps = {
onScanComplete: (scannedValue: string) => void;
};
const { width, height } = Dimensions.get("window");
const overlaySize = width * 0.7;
export default function QRCodeScanner({ onScanComplete }: QRCodeScannerProps) {
const device = useCameraDevice("back");
const { hasPermission, requestPermission } = useCameraPermission();
// Ref to track if a scan has been handled
const scanHandledRef = useRef(false);
const codeScanner = useCodeScanner({
codeTypes: ["qr"],
onCodeScanned: ([code]) => {
if (code?.value && !scanHandledRef.current) {
console.log("Scanned QR Code:", code.value);
scanHandledRef.current = true;
onScanComplete(code.value);
}
},
});
// Handle cases where permissions are not granted
if (!hasPermission) {
return (
Request Camera Permission
);
}
// Handle cases where no camera device is found
if (!device) {
return (
No back camera device found.
);
}
return (
{/* Render the Camera component */}
{/* QR Code Overlay */}
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
centered: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
camera: {
flex: 1,
},
errorText: {
fontSize: 16,
color: "red",
},
overlayContainer: {
position: "absolute",
top: 0,
left: 0,
width: width,
height: height,
justifyContent: "space-between",
alignItems: "center",
},
topOverlay: {
width: width,
height: (height - overlaySize) / 2,
backgroundColor: "rgba(0, 0, 0, 0.5)",
},
middleOverlay: {
flexDirection: "row",
},
sideOverlay: {
width: (width - overlaySize) / 2,
height: overlaySize,
backgroundColor: "rgba(0, 0, 0, 0.5)",
},
focusedArea: {
width: overlaySize,
height: overlaySize,
borderWidth: 2,
borderColor: "blue",
backgroundColor: "transparent",
},
bottomOverlay: {
width: width,
height: (height - overlaySize) / 2,
backgroundColor: "rgba(0, 0, 0, 0.5)",
},
button: {
backgroundColor: "#007AFF",
paddingVertical: 12,
paddingHorizontal: 20,
borderRadius: 8,
alignItems: "center",
},
buttonText: {
color: "white",
fontSize: 16,
fontWeight: "600",
},
});
```
This component uses the `react-native-vision-camera` library to provide QR code scanning
functionality with the following features:
* **Camera Integration**: Uses the device's camera and handles permission requests.
* **Visual Guidance**: Displays an overlay with a focused scanning area to help users position QR
codes correctly,
* **Error Handling**: Provides clear UI feedback when:
* Camera permissions haven't been granted,
* No compatible camera device is found,
* **Scan Processing**: Captures QR code data and passes it to the parent component via the
`onScanComplete` callback,
3. Next, return to the `index.tsx` file and import the `QRCodeScanner` component by adding the following code
under the `// Claim a Credential - Step 2.3: Import the QRCodeScanner component` comment:
```ts title="/app/index.tsx"
import QRCodeScanner from "@/components/QRCodeScanner";
```
When the `QRCodeScanner` decodes the QR code, it will call the `onScanComplete` callback with the
scanned value. This should be a URI that starts with `openid-credential-offer://`.
4. Add the following code under the
`// Claim a Credential - Step 2.4: Define the handleScanComplete function` comment to handle the
scanned QR code:
```ts title="/app/index.tsx"
const handleScanComplete = (scannedValue: string) => {
setIsScannerVisible(false);
if (!scannedValue) return;
if (scannedValue.startsWith("openid-credential-offer://")) {
router.push({
pathname: "/claim-credential",
params: { scannedValue },
});
}
// Online Presentation - Step 2.2: Handle the 'mdoc-openid4vp://' scheme prefix
};
```
When called, the `handleScanComplete` function would redirect the user to the
`/claim-credential` screen with the `scannedValue` parameter obtained from the QR code. This is
the screen where we will display the offer details to the user and enable them to accept the
offered credential.
Your code editor will likely show a warning as we will only create the
`/claim-credential` screen later in the tutorial.
5. Add the following code under the
`{/* Claim a Credential - Step 2.5: Add the QRCodeScanner component */}` comment to combine the
`QRCodeScanner` component with the `handleScanComplete` function:
```ts title="/app/index.tsx"
setIsScannerVisible(false)}
>
setIsScannerVisible(false)}
>
Close Scanner
```
Now, when the `Claim Credential` button is selected the app will display the `QRCodeScanner`
component in a modal. When a QR code is successfully scanned, the `onScanComplete` callback
(`handleScanComplete`) is triggered, which will navigate the user to the `/claim-credential` screen
with the information obtained from the QR code passed as a `scannedValue` parameter.
6. Run the app.
The application will now display a **Claim Credential** button on the home screen. As the user
selects this button they will be prompted for permission to access the camera, and then the QR code
scanner will be displayed.
You should see a result similar to the following:
### Step 3: Retrieve offer details and present them to the user [#step-3-retrieve-offer-details-and-present-them-to-the-user]
Next, you'll add the ability to display the details of the Credential offer to the user before they
decide to claim any credentials. This process, known as *credential discovery*, allows your wallet
application to retrieve and present the offer details, including:
* What Issuer is offering the credentials?
* What credentials are being offered, in what format and what claims do they include?
To display this information to the user, your application should call the SDK's
[`discoverCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/discovercredentialoffer\(_:\))
method. We are going to implement this within the `ViewModel` class.
1. Add the following code under the
`// Claim Credential - Step 3.1: Add DiscoveredCredentialOffer and discoveredCredentialOfferURL vars`
comment to add new variables that will hold the credential offer details:
```swift title="ContentView"
var discoveredCredentialOffer: DiscoveredCredentialOffer?
var discoveredCredentialOfferURL = ""
```
2. Replace the `print` statement under the
`// Claim Credential - Step 3.2: Add discover credential offer logic` comment with the following
code to create a function that calls the SDK's
[`discoverCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/discovercredentialoffer\(_:\))
method:
```swift title="ContentView"
Task {
do {
discoveredCredentialOffer = try await mobileCredentialHolder.discoverCredentialOffer(offer)
// save the url to use for credential retrieval
discoveredCredentialOfferURL = offer
// present credential offer screen, as soon as credential offer is discovered
navigationPath.append(NavigationState.credentialOffer)
} catch {
print(error)
}
}
```
This function is called from our `QRScannerView` callback, so that when the user scans a QR Code
that includes a credential offer, the
[`discoverCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/discovercredentialoffer\(_:\))
method is called and accepts the returned `credentialOffer` string as its `offer` parameter.
This is a URL-encoded
[Credential offer](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-credential-offer)
which in our example is embedded in a QR code. In other implementations you might have to
retrieve this parameter from a deep-link.
The
[`discoverCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/discovercredentialoffer\(_:\))
method makes a request to the `offer` URL to retrieve the offer details and returns it as a
[`DiscoveredCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/discoveredcredentialoffer)
object:
```swift title="Swift"
struct DiscoveredCredentialOffer {
let issuer: URL
let credentials: [OfferedCredential]
let transactionCode: TransactionCode?
}
```
The application can now use the `issuer` and `credentials` properties and present this
information for the user to review. Once an application has discovered a credential offer, the
user is navigated to the `credentialOfferView` view, which you are going to implement next.
Next you will use the `transactionCode` property to inspect whether or not the issuer requires a
transaction code to claim the credential. This will enable the app to handle offers with and
without a transaction code.
3. Create a new file named `transactionCodeInputView` and paste the following code to create a view
that allows the user to input a transaction code when it is required by the issuer:
```swift title="transactionCodeInputView"
import SwiftUI
struct TransactionCodeInputView: View {
var viewModel: ViewModel
@State private var transactionCode = ""
@Environment(\.dismiss) private var dismiss
var body: some View {
VStack(spacing: 20) {
Text("Transaction Code Required")
.font(.title2)
.fontWeight(.bold)
Text("Please enter the transaction code to proceed with credential retrieval.")
.multilineTextAlignment(.center)
.foregroundColor(.secondary)
TextField("Enter transaction code", text: $transactionCode)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding(.horizontal)
HStack(spacing: 20) {
Button("Cancel") {
dismiss()
}
.buttonStyle(.bordered)
Button("Retrieve Credentials") {
viewModel.retrieveCredential(transactionCode: transactionCode)
}
.buttonStyle(.borderedProminent)
.disabled(transactionCode.isEmpty)
}
Spacer()
}
.padding()
.navigationTitle("Transaction Code")
.navigationBarBackButtonHidden(false)
}
}
```
The application will only show this view when the credential offer indicates a transaction code
is required. The user will then be able to input a transaction code they had received separately
from the issuer.
4. Return to `ContentView` file and replace the `EmptyView` under the
`// Claim Credential - Step: 3.4 Display transaction code input view` comment with the following
code to make use of the new view:
```swift title="Swift"
TransactionCodeInputView(viewModel: viewModel)
```
5. Replace the `EmptyView` under the `// Claim Credential - Step 3.5: Display Credential offer`
comment with the following code to navigate the user to the `credentialOfferView` view when a
credential offer is discovered:
```swift title="ContentView"
VStack {
Text("Received \(viewModel.discoveredCredentialOffer?.credentials.count ?? 0) Credential Offer(s)")
.font(.headline)
Text("from \(viewModel.discoveredCredentialOffer?.issuer.absoluteString ?? "unknown issuer")")
.font(.subheadline)
List(viewModel.discoveredCredentialOffer?.credentials ?? [], id: \.docType) { credential in
Section {
HStack {
Text("Name:")
.bold()
Spacer()
Text("\(credential.name ?? "")")
}
HStack {
Text("Doctype:")
.bold()
Spacer()
Text("\(credential.docType)")
}
HStack {
Text("No. of claims:")
.bold()
Spacer()
Text("\(credential.claims?.count ?? 0)")
}
}
}
Button {
if viewModel.discoveredCredentialOffer?.transactionCode != nil {
viewModel.navigationPath.append(NavigationState.transactionCodeInput)
return
}
viewModel.retrieveCredential(transactionCode: nil)
} label: {
Text("Consent and retrieve Credential(s)")
.font(.title3)
}
.buttonStyle(.borderedProminent)
.clipShape(Capsule())
}
```
The app now handles selection of the **Consent and retrieve Credential(s)** button based on the
retrieved offer details:
* For Pre-authorized Code offers:
* If a transaction code is required, it will navigate the user to the
`TransactionCodeInputView` view.
* If no transaction code is required, it will call
`viewModel.retrieveCredential(transactionCode: nil)` to retrieve the credential.
* For Authorization Code offers:
* The SDK will automatically redirect the user to a web browser to authenticate with issuer
before continuing to retrieve the credential.
6. [Run](https://developer.apple.com/documentation/xcode/running-your-app-in-simulator-or-on-a-device)
the app, select the **Claim Credential** button and scan the following QR code:
You should see a result similar to the following:
As the user scans the QR code, the application displays the credential offer details.
You might notice that nothing happens if you select the **Consent and retrieve Credential(s)**
button. This is expected - in the next step you will implement the logic that initiates the
credential issuance once the user provides their consent.
To display this information to the user, your application should call the
[`discoverCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/discover-credential-offer.html)
function.
1. In your `MainActivity` file, add the following code under the
`// Claim Credential - Step 3.1: Add discovered credential offer variables` comment to add new
variables that will hold the credential offer details:
```kotlin title="MainActivity.kt"
var scannedOffer: String? = null
var discoveredCredentialOffer: DiscoveredCredentialOffer? = null
```
2. In your `ScanOfferScreen` file, add the following code under the
`// Step 3.2: Discover credential offer` comment to handle the credential offer upon scanning a
QR code:
```kotlin title="ScanOfferScreen.kt"
try {
SharedData.scannedOffer = offer
SharedData.discoveredCredentialOffer =
MobileCredentialHolder.getInstance().discoverCredentialOffer(offer)
} catch (e: Exception) {
Toast.makeText(context, "Failed to discover offer", Toast.LENGTH_SHORT).show()
}
navController.navigateUp()
```
Now, once the user scans a QR Code, the
[`discoverCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/discover-credential-offer.html)
function is called and accepts the returned `offer` string as its parameter.
This is a URL-encoded
[Credential offer](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-credential-offer)
which in our example was embedded in a QR code. In other implementations you might have to
retrieve this parameter from a deep-link.
The
[`discoverCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/discover-credential-offer.html)
function makes a request to the `offer` URL to retrieve the offer information and returns it as
a
[`DiscoveredCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.issuance.dto/-discovered-credential-offer/index.html)
object:
```text title="DiscoveredCredentialOffer structure"
DiscoveredCredentialOffer {
issuer: String,
credentials: [OfferedCredential],
transactionCode: TransactionCode?
}
```
* The application can now use the `issuer` and `credentials` properties and present this
information for the user to review.
* The `transactionCode` property is optional and is only used in the Pre-authorized Code flow
when the issuer configures it as part of the issuance flow.
3. In your `MainActivity` file, add the following code under the
`// Claim Credential - Step 3.3: Display discovered credential offer` to display the offer
details to the user:
```kotlin title="MainActivity.kt"
SharedData.discoveredCredentialOffer?.let { discoveredOffer ->
Text("Received Credential Offer from ${discoveredOffer.issuer}")
LazyColumn(Modifier.fillMaxWidth()) {
items(discoveredOffer.credentials, key = { it.docType }) { credential ->
Card(Modifier.fillMaxWidth()) {
Column(Modifier.padding(4.dp)) {
Text("Name: ${credential.name ?: ""}")
Text("DocType: ${credential.docType}")
}
}
}
}
// Claim Credential - Step 4.3: Add transaction code input
// Claim Credential - Step 4.5: Add consent button
}
```
4. [Run](https://developer.android.com/studio/run#basic-build-run) the app, select the **Scan
Credential Offer** button and scan the following QR code:
You should see a result similar to the following:
As the user scans the QR code, the application displays the Credential offer details.
To display this information to the user, your holder application should call the SDK's
[`discoverCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/discoverCredentialOffer.html)
function, passing the OID4VCI credential offer endpoint's URI as its `uri` parameter. This URI is
retrieved by the application from the scanned QR code.
1. Create a new file named `/app/claim-credential.tsx` and add the following scaffolding code:
```ts title="/app/claim-credential.tsx"
import { useGlobalSearchParams, useRouter } from "expo-router";
import React, { useCallback, useEffect, useState } from "react";
import { ActivityIndicator, Alert, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View } from "react-native";
import { useHolder } from "@/providers/HolderProvider";
import {
discoverCredentialOffer,
retrieveCredentials,
} from "@mattrglobal/mobile-credential-holder-react-native";
import type {
DiscoveredCredentialOffer,
OfferedCredential,
} from "@mattrglobal/mobile-credential-holder-react-native/lib/types/index";
// Claim a Credential - Step 4.1: define the CLIENT_ID constant
export default function ClaimCredential() {
const router = useRouter();
const { scannedValue } = useGlobalSearchParams<{ scannedValue: string }>();
const { isHolderInitialized, getMobileCredentials } = useHolder();
const [credentialOffer, setCredentialOffer] = useState();
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
const [transactionCode, setTransactionCode] = useState("");
useEffect(() => {
const discoverOffer = async () => {
if (!isHolderInitialized || !scannedValue) {
setError("Missing holder instance or scanned value.");
setIsLoading(false);
return;
}
// Claim Credential - Step 3.2: Discover Credential Offer
};
discoverOffer();
}, [isHolderInitialized, scannedValue]);
const handleConsent = useCallback(async () => {
if (!credentialOffer || !isHolderInitialized) {
Alert.alert("Error", "Missing offer information or holder instance.");
router.back();
return;
}
// Claim a Credential - Step 4.2: Retrieve Credentials
}, [credentialOffer, isHolderInitialized, getMobileCredentials, router, scannedValue, transactionCode]);
if (isLoading) {
return (
Discovering offer...
);
}
if (error || !credentialOffer) {
return (
Error: {error || "No discovery offer found."}
router.back()}
>
Go Back
);
}
// Claim a Credential - Step 3.3: Display the offer details to the user
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
backgroundColor: "#fff",
},
centered: {
flex: 1,
alignItems: "center",
justifyContent: "center",
},
title: {
fontSize: 22,
fontWeight: "600",
marginBottom: 10,
color: "#000",
},
text: {
fontSize: 16,
color: "#333",
marginBottom: 10,
},
card: {
marginBottom: 16,
padding: 10,
backgroundColor: "#F5F5F5",
borderRadius: 8,
borderWidth: 1,
borderColor: "#E0E0E0",
},
cardTitle: {
fontWeight: "700",
fontSize: 18,
marginBottom: 5,
color: "#000",
},
button: {
backgroundColor: "#007AFF",
paddingVertical: 12,
paddingHorizontal: 20,
borderRadius: 8,
alignItems: "center",
},
buttonText: {
color: "white",
fontSize: 16,
fontWeight: "600",
},
buttonContainer: {
marginBottom: 20,
gap: 10,
},
errorText: {
color: "#FF3B30",
textAlign: "center",
},
transactionCodeContainer: {
marginBottom: 16,
padding: 16,
backgroundColor: "#F8F9FA",
borderRadius: 8,
borderWidth: 1,
borderColor: "#E0E0E0",
},
description: {
fontSize: 14,
color: "#666",
marginBottom: 8,
fontStyle: "italic",
},
textInput: {
borderWidth: 1,
borderColor: "#DDD",
borderRadius: 8,
padding: 12,
fontSize: 16,
backgroundColor: "white",
marginTop: 8,
},
});
```
2. Add the following code under the `// Claim a Credential - Step 3.2: Discover Credential Offer`
comment to call the SDK's
[`discoverCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/discoverCredentialOffer.html)
function and discover the credential offer:
```ts title="/app/claim-credential.tsx"
const discoveryResult = await discoverCredentialOffer(scannedValue);
if (discoveryResult.isErr()) {
setError(`Error discovering credential offer: ${discoveryResult.error.message || discoveryResult.error.type}`);
} else {
console.log("Discovered Credential Offer:", discoveryResult.value);
setCredentialOffer(discoveryResult.value);
}
setIsLoading(false);
```
Now, once the user scans a QR Code that includes a credential offer (e.g. it is prefixed with
`openid-credential-offer://`), the SDK's
[`discoverCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/discoverCredentialOffer.html)
function is called and accepts the returned `scannedValue` string (Offer URI) as its `uri`
parameter.
This is a URL-encoded
[Credential offer](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-credential-offer)
which in our example was embedded in a QR code. In other implementations you might have to
retrieve this parameter from a deep-link.
The
[`discoverCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/discoverCredentialOffer.html)
function makes a request to the `uri` URL to retrieve the offer information and returns it as a
[`DiscoveredCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/types/DiscoveredCredentialOffer.html)
object:
```ts title="DiscoveredCredentialOffer structure"
DiscoveredCredentialOffer: {
issuer: string;
credentials: OfferedCredential[];
transactionCode?: {
description?: string;
length?: number;
};
}
```
Your application can now use the `issuer` (who is offering the credentials) and `credentials`
(what credentials are offered and what claims they contain) properties and present this
information for the user to review.
3. Add the following code under the
`// Claim a Credential - Step 3.3: Display the offer details to the user` comment to display the
offer details to the user:
```ts title="/app/claim-credential.tsx"
return (
Credential Offer
Received {credentialOffer.credentials.length} credential
{credentialOffer.credentials.length > 1 ? "s" : ""} from {credentialOffer.issuer}
{/* Claim a Credential - Step 4.3: Add transaction code input if required */}
{credentialOffer?.transactionCode && (
Transaction Code Required:
{credentialOffer.transactionCode.description && (
{credentialOffer.transactionCode.description}
)}
)}
{credentialOffer.credentials.map((cred: OfferedCredential, index: number) => (
{cred.name}Document Type: {cred.docType}Number of Claims: {cred.claims?.length}
))}
{/* Claim a Credential - Step 4.4: Add the Consent and Retrieve button */}
);
```
4. Run the app, select the **Claim Credential** button and scan the following QR code:
You should see a result similar to the following:
As the user scans the QR code, the holder application displays the Credential offer details.
### Step 4: Obtain user consent and initiate credential issuance [#step-4-obtain-user-consent-and-initiate-credential-issuance]
The next (and final!) step is to build the capability for the user to accept the credential offer.
This should then trigger issuing the credential and storing it in the application storage.
Once the user provides their consent by selecting the **Consent and retrieve Credential(s)** button,
your application must call the SDK's
[`retrieveCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/preview/documentation/mobilecredentialholdersdk/mobilecredentialholder/retrievecredentials\(credentialoffer:clientid:transactioncode:\))
function to trigger the credential issuance and store the issued credential in the application
storage.
* For **Pre-authorized Code offers**, this will happen within the application and after the user
provided a transaction code (when required by the issuer).
* For **Authorization Code offers**, this will happen after the user had completed authentication
with the issuer and was redirected back to the application.
1. Add the following code under the `// Claim Credential - Step 4.1: Add retrievedCredentials var`
comment to add a new variable that will hold the result returned by the SDK's
[`retrieveCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/preview/documentation/mobilecredentialholdersdk/mobilecredentialholder/retrievecredentials\(credentialoffer:clientid:transactioncode:\))
method:
```swift title="ContentView"
var retrievedCredentials: [MobileCredential] = []
```
2. Replace the `print` statement under the
`// Claim Credential - Step 4.2: Call retrieveCredential method` comment with the following code
to create a new function that will call the SDK's
[`retrieveCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/preview/documentation/mobilecredentialholdersdk/mobilecredentialholder/retrievecredentials\(credentialoffer:clientid:transactioncode:\))
method:
```swift title="ContentView"
Task {
do {
let retrievedCredentialResults = try await mobileCredentialHolder.retrieveCredentials(
credentialOffer: discoveredCredentialOfferURL,
clientId: Constants.clientId,
transactionCode: transactionCode
)
Task {
var credentials: [MobileCredential] = []
for result in retrievedCredentialResults {
switch result {
case .success(_, let credentialId):
if let credential = try? await mobileCredentialHolder.getCredential(credentialId: credentialId) {
credentials.append(credential)
}
case .failure(let docType, let error):
print("Failed to retrieve \(docType): \(error)")
}
}
self.retrievedCredentials = credentials
// Clear navigation stack and display retrievedCredentials view
navigationPath = NavigationPath()
navigationPath.append(NavigationState.retrievedCredentials)
}
} catch {
print(error.localizedDescription)
}
}
```
Let’s review the parameters passed to the `retrieveCredentials` function:
* `credentialOffer`: This is the same credential offer string from the QR Code that we used to call
[`discoverCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/discovercredentialoffer\(_:\))
with.
* `clientId`: This was configured when
[setting up your development environment](#configure-required-resources). It is used by the issuer
to identify the wallet application that is making a request to claim credentials.
* `transactionCode`: This is only required for Pre-authorized Code credential offers. If your
application is only using Authorization Code flows offers, you should set it to `nil`.
The
[`retrieveCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/preview/documentation/mobilecredentialholdersdk/mobilecredentialholder/retrievecredentials\(credentialoffer:clientid:transactioncode:\))
function returns an array of
[`RetrieveCredentialResult`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/retrievecredentialresult)
objects. Each object contains metadata about a credential that was retrieved:
```swift title="RetrieveCredentialResult"
enum RetrieveCredentialResult {
case success(docType: String, credentialId: String)
case failure(docType: String, error: RetrieveCredentialError)
}
[
{
"docType":"org.iso.18013.5.1.mDL",
"credentialId":"F52084CF-8270-4577-8EDD-23149639D985"
}
]
```
`RetrieveCredentialResult` is an enum with two cases, each carrying guaranteed values:
* `.success` : Carries the `docType` and the `credentialId` (the internally unique identifier) of a
successfully retrieved credential.
* `.failure` : Carries the `docType` and the `error` describing why retrieval failed.
After the result is received, your application can retrieve specific credentials by calling the
SDK's
[`getCredential`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/getcredentials\(\))
method with the `credentialId` of any retrieved credential.
The SDK's
[getCredential](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/getcredentials\(\))
method returns a
[`MobileCredential`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredential)
object which can be used to display the retrieved credential, its claims and verification status to
the user.
Since this object can be used across multiple views, it will make sense to create one view that will
represent it. We will use this view in both the
[Proximity](/docs/holding/proximity-presentation-tutorial) and
[Online](/docs/holding/remote-presentation-tutorial) presentation tutorials.
3. Create a new file named `DocumentView` and add the following content:
```swift title="DocumentView"
import MobileCredentialHolderSDK
import SwiftUI
struct DocumentView: View {
var viewModel: DocumentViewModel
var body: some View {
VStack(alignment: .leading, spacing: 10) {
Text(viewModel.docType)
.font(.title)
.fontWeight(.bold)
.padding(.bottom, 5)
ForEach(viewModel.namespacesAndClaims.keys.sorted(), id: \.self) { key in
VStack(alignment: .leading, spacing: 5) {
Text(key)
.font(.headline)
.padding(.vertical, 5)
.padding(.horizontal, 10)
.background(Color.gray.opacity(0.2))
.cornerRadius(5)
ForEach(viewModel.namespacesAndClaims[key]!.keys.sorted(), id: \.self) { claim in
HStack {
Text(claim)
.fontWeight(.semibold)
Spacer()
Text(viewModel.namespacesAndClaims[key]![claim]! ?? "")
.fontWeight(.regular)
}
.padding(.vertical, 5)
.padding(.horizontal, 10)
.background(Color.white)
.cornerRadius(5)
.shadow(radius: 1)
}
}
.padding(.vertical, 5)
}
}
.padding()
.background(RoundedRectangle(cornerRadius: 10).fill(Color.white).shadow(radius: 5))
.padding(.horizontal)
}
}
// MARK: DocumentViewModel
class DocumentViewModel {
var docType: String
var namespacesAndClaims: [String: [String: String?]]
init(from credential: MobileCredential) {
self.docType = credential.docType
self.namespacesAndClaims = credential.claims.reduce(into: [String: [String: String]]()) { result, outerElement in
let (outerKey, innerDict) = outerElement
result[outerKey] = innerDict.mapValues { $0.textRepresentation }
}
}
init(from credentialMetadata: MobileCredentialMetadata) {
self.docType = credentialMetadata.docType
var result: [String: [String: String?]] = [:]
credentialMetadata.claims.forEach { namespace, claimIDs in
var transformedClaims: [String: String?] = [:]
claimIDs.forEach { claimID in
transformedClaims[claimID] = Optional.none
}
result[namespace] = transformedClaims
}
self.namespacesAndClaims = result
}
init(from request: MobileCredentialRequest) {
self.docType = request.docType
self.namespacesAndClaims = request.namespaces.reduce(into: [String: [String: String?]]()) { result, outerElement in
let (outerKey, innerDict) = outerElement
result[outerKey] = innerDict.mapValues { _ in nil }
}
}
}
// MARK: Helper
extension MobileCredentialElementValue {
var textRepresentation: String {
switch self {
case .bool(let bool):
return "\(bool)"
case .string(let string):
return string
case .int(let int):
return "\(int)"
case .unsigned(let uInt):
return "\(uInt)"
case .float(let float):
return "\(float)"
case .double(let double):
return "\(double)"
case let .date(date):
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .none
return dateFormatter.string(from: date)
case let .dateTime(date):
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .short
return dateFormatter.string(from: date)
case .data(let data):
return "Data \(data.count) bytes"
case .map(let dictionary):
let result = dictionary.mapValues { value in
value.textRepresentation
}
return "\(result)"
case .array(let array):
return array.reduce("") { partialResult, element in
partialResult + element.textRepresentation
}
.appending("")
@unknown default:
return "Unknown type"
}
}
}
```
The file comprises the following components:
* `DocumentViewModel`: This class stores the credentials' docType and claim values.
* `DocumentView`: This view takes `DocumentViewModel` as a parameter and displays its content in
a human-readable format.
* `MobileCredentialElementValue` : This helper extension allows retrieving a
`MobileCredentialElementValue` from a `MobileCredential`'s `claims` and present it in a
human-readable format.
4. Return to `ContentView` and replace `EmptyView` under the
`// Claim Credential - Step 4.4: Display retrieved credentials` comment with the following code
to use the `DocumentView` structure to display retrieved credentials to the user:
```swift title="ContentView"
ScrollView {
VStack {
Text("Retrieved Credentials")
.font(.title)
ForEach(viewModel.retrievedCredentials, id: \.id) { credential in
DocumentView(viewModel: DocumentViewModel(from: credential))
}
}
}
```
Once the app calls the `retrieveCredentials` function, the SDK processes the response based on the
type of credential offer retrieved:
* In the **Authorization Code flow**:
* The user is redirected to
[authenticate](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-10.html#name-authorized-code-flow)
with the configured Authentication provider defined in the `authorizeEndpoint` element of the
`DiscoveredCredentialOffer` object.
* Upon successful authentication, the user can proceed to complete the
[OID4VCI workflow](/docs/issuance/oid4vci-overview) configured by the issuer. This workflow can include
different steps based on the issuer’s configuration, but eventually the user is redirected to
the configured `redirectUri` which should be handled by your application.
* In the **Pre-authorized Code flow** the user is not redirected out of the application, but rather
provides a transaction code (when required by the issuer) and the immediately proceeds to claiming
the credential. If no transaction code is required, the user can claim the credential immediately
after selecting the **Consent and retrieve Credential(s)** button.
The issuer then sends the issued mDocs to your application, and the SDK processes and validates them
against the [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html) standard. Credentials
who meet validation rules are stored in the application internal data storage.
In our example this is achieved by selecting a **Consent and retrieve Credential(s)** button. Once
the user provides their consent by selecting this button, your application must call the
[`retrieveCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/retrieve-credentials.html)
function to trigger the credential issuance.
* For **Pre-authorized Code offers**, this will happen within the application and after the user
provided a transaction code (when required by the issuer).
* For **Authorization Code offers**, this will happen after the user had completed authentication
with the issuer and was redirected back to the application.
1. In your `MainActivity` file, add the following code under the
`// Step 4.1: Add credential issuance configuration` comment to configure the redirect URI that
will be used to redirect the user back to your application after completing the authentication:
```kotlin title="MainActivity.kt"
credentialIssuanceConfiguration = CredentialIssuanceConfiguration(
redirectUri = "io.mattrlabs.sample.mobilecredentialtutorialholderapp:" +
"//credentials/callback",
autoTrustMobileCredentialIaca = true
)
```
* `redirectUri` is constructed of the same `scheme` and `host` values you have
[set](#add-required-dependencies) in the `AndroidManifest.xml` file.
* This configuration is only required for the Authorization Code flow offers. If your
application is only using Pre-authorized Code flow offers you can remove it.
2. Add the following code under the
`// Claim Credential - Step 4.2: Add retrieved credentials variable` comment to add a new
variable that will hold the result returned by the
[`retrieveCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/retrieve-credentials.html)
function:
```kotlin title="MainActivity.kt"
var retrievedCredentials: List = emptyList()
```
3. Add the following code under the `// Claim Credential - Step 4.3: Add transaction code input`
comment to add a new text input field to allow the user to provide a transaction code when one is
provided with a Pre-authorized Code flow offer:
```kotlin title="MainActivity.kt"
if (discoveredOffer.transactionCode != null) {
OutlinedTextField(
value = transactionCode,
onValueChange = { transactionCode = it },
label = { Text("Transaction Code") },
modifier = Modifier.fillMaxWidth()
)
}
```
This is only required for Pre-authorized Code flow offers. If your application should only
handle Authorization Code flow offers you can remove it.
4. Add the following code under the
`// Claim Credential - Step 4.4: Create function to retrieve credentials` comment to create a new
function that will call the
[`retrieveCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/retrieve-credentials.html)
method when the user gives the consent for the credentials retrieval:
```kotlin title="MainActivity.kt"
private fun onRetrieveCredentials(
coroutineScope: CoroutineScope,
activity: Activity,
navController: NavController,
transactionCode: String
) {
coroutineScope.launch {
try {
val mdocHolder = MobileCredentialHolder.getInstance()
val retrieveCredentialResults = mdocHolder.retrieveCredentials(
activity,
SharedData.scannedOffer!!, // [!code highlight]
clientId = "android-mobile-credential-tutorial-holder-app", // [!code highlight]
transactionCode = transactionCode // [!code highlight]
)
// Claim Credential - Step 4.6: Display retrieved credentials
} catch (e: Exception) {
Toast.makeText(activity, "Failed to retrieve credentials", Toast.LENGTH_SHORT).show()
}
}
}
```
* `SharedData.scannedOffer` is the same offer String that was used for the
[`discoverCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/discover-credential-offer.html)
function call.
* `clientId` is used by the issuer to recognize the application. This is only used internally in
the interaction between the application and the issuer and can be any string as long as it is
registered with the issuer as a trusted application.
* `transactionCode` is only required for Pre-authorized Code credential offers. If your
application is only using Authorization Code flows offers you can remove it.
5. Add the following code under the `// Claim Credential - Step 4.5: Add consent button` comment to
add a button for calling the new function:
```kotlin title="MainActivity.kt"
Spacer(Modifier.weight(1f))
Button(
onClick = {
onRetrieveCredentials(coroutineScope, activity, navController, transactionCode)
},
Modifier.fillMaxWidth()
) { Text("Consent and retrieve Credential(s)") }
```
The
[`retrieveCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/retrieve-credentials.html)
function then returns a
[`RetrieveCredentialResult`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.issuance.dto/-retrieve-credential-result/index.html)
list, which references all retrieved credentials:
```kotlin title="RetrieveCredentialResult structure"
[RetrieveCredentialResult]
sealed interface RetrieveCredentialResult {
val docType: String
data class Success(
override val docType: String,
val credentialId: String
) : RetrieveCredentialResult
data class Failure(
override val docType: String,
val error: RetrieveCredentialError
) : RetrieveCredentialResult
}
[
{
"docType":"org.iso.18013.5.1.mDL", // [!code highlight]
"credentialId":"F52084CF-8270-4577-8EDD-23149639D985" // [!code highlight]
}
]
```
`RetrieveCredentialResult` is a sealed interface with two variants, each carrying guaranteed
properties:
* `Success` : Carries the `docType` and the `credentialId` (UUID) of a successfully retrieved
credential.
* `Failure` : Carries the `docType` and the `error` describing why retrieval failed.
Your application can now retrieve specific credentials by calling the
[`getCredential`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/get-credential.html)
function with the `credentialId` of any of the retrieved credentials.
The
[getCredential](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/get-credential.html)
function returns a
[`MobileCredential`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.dto/-mobile-credential/index.html)
object which represents the issued mDoc, and your application can now introduce UI elements to
enable the user to view the credential.
6. Add the following code under the `// Claim Credential - Step 4.6: Display retrieved credentials`
comment to retrieve the credentials by their IDs from the local storage, save them to the
`SharedData.retrievedCredentials` variable and navigate to the `retrievedCredential` screen to
display the retrieved credential:
```kotlin title="MainActivity.kt"
SharedData.retrievedCredentials = retrieveCredentialResults.mapNotNull { result ->
when (result) {
is RetrieveCredentialResult.Success -> try {
mdocHolder.getCredential(result.credentialId, fetchUpdatedStatusList = false)
} catch (e: Exception) {
val msg = "Failed to get credential from storage"
Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show()
null
}
is RetrieveCredentialResult.Failure -> {
val msg = "Failed to retrieve ${result.docType}: ${result.error}"
Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show()
null
}
}
}
navController.navigate("retrievedCredential")
SharedData.discoveredCredentialOffer = null
```
7. In your package, create a new file named `RetrievedCredentialsScreen.kt`.
8. Add the following code to the new file to display the `docType` and `claims` of retrieved
credentials to the user:
```kotlin title="RetrievedCredentialsScreen.kt"
package com.example.holdertutorial
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import global.mattr.mobilecredential.holder.deviceretrieval.deviceresponse.NameSpace
import global.mattr.mobilecredential.holder.dto.MobileCredentialElement
@Composable
fun RetrievedCredentialsScreen() {
if (SharedData.retrievedCredentials.isEmpty()) {
Text("No credentials received")
} else {
Column {
Text(
"Retrieved Credentials",
modifier = Modifier.fillMaxWidth(),
style = MaterialTheme.typography.titleLarge
)
LazyColumn(Modifier.fillMaxWidth()) {
items(SharedData.retrievedCredentials, key = { it.id }) { credential ->
Document(
credential.docType,
credential.claims.mapValues { (_, claims) ->
claims.map { (name, value) -> "$name: ${value.toUiString()}" }.toSet()
}
)
}
}
}
}
}
@Composable
fun Document(docType: String, namespacesAndClaims: Map>) {
Column(Modifier.fillMaxWidth().padding(6.dp)) {
Text(docType, Modifier.padding(6.dp), style = MaterialTheme.typography.titleMedium)
namespacesAndClaims.forEach { (namespace, claims) ->
Text(namespace, Modifier.padding(6.dp), style = MaterialTheme.typography.titleSmall)
Column(
Modifier
.padding(6.dp)
.fillMaxWidth()
.background(MaterialTheme.colorScheme.background, RoundedCornerShape(6.dp))
.padding(6.dp)
) {
claims.forEach { claim -> Text(claim) }
}
}
}
}
fun MobileCredentialElement.toUiString() = when (this) {
is MobileCredentialElement.ArrayElement, is MobileCredentialElement.DataElement,
is MobileCredentialElement.MapElement -> this::class.simpleName ?: "Unknown element"
else -> value.toString()
}
```
9. Back in your `MainActivity` file, add the following code under the
`// Claim Credential - Step 4.9: Add "Retrieved Credential" screen call` comment to connect the
created composable to the navigation graph:
```kotlin title="MainActivity.kt"
RetrievedCredentialsScreen()
```
Once the app calls the `retrieveCredentials` function, the SDK processes the response based on the
type of credential offer retrieved:
* In the **Authorization Code flow**:
* The user is redirected to
[authenticate](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-10.html#name-authorized-code-flow)
with the configured Authentication provider defined in the `authorizeEndpoint` element of the
`DiscoveredCredentialOffer` object.
* Upon successful authentication, the user can proceed to complete the
[OID4VCI workflow](/docs/issuance/oid4vci-overview) configured by the issuer. This workflow can include
different steps based on the issuer’s configuration, but eventually the user is redirected to
the configured `redirectUri` which should be handled by your application.
* In the **Pre-authorized Code flow** the user is not redirected out of the application, but rather
provides a transaction code (when required by the issuer) and the immediately proceeds to claiming
the credential. If no transaction code is required, the user can claim the credential immediately
after selecting the **Consent and retrieve Credential(s)** button.
The issuer then sends the issued mDocs to your application, and the SDK processes and validates them
against the [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html) standard. Credentials
who meet validation rules are stored in the application internal data storage.
We will implement this by adding a **Consent and Retrieve** button. Once the user provides their consent by
selecting this button, your application must call the
[`retrieveCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/retrieveCredentials.html)
function to trigger the credential issuance.
* For **Pre-authorized Code offers**, this will happen within the application and after the user provided a transaction code (when required by the issuer).
* For **Authorization Code offers**, this will happen after the user had completed authentication with the issuer and was redirected back to the application.
1. Add the following constant variables to represent the Authentication provider we will use for
this tutorial under the
`// Claim a Credential - Step 4.1: define the CLIENT_ID constant` comment:
```ts title="/app/claim-credential.tsx"
const CLIENT_ID = "react-native-mobile-credential-holder-tutorial-app";
```
* `CLIENT_ID` : This is the identifier that is used by the issuer to recognize the holder
application. This is only used internally in the interaction between the holder
application and the issuer and can be any string as long as it is registered with the issuer as a
trusted holder application.
The `CLIENT_ID` and `redirectUri` (configured during [SDK
initialization](#step-1-initialize-the-sdk)) are both required parameters for
the OID4VCI Authorization Code workflow. For this tutorial you will be
claiming a credential from a MATTR Labs issuer which is pre-configured with
the correct parameters. We will help you configure your unique values as you
move your implementation into production.
2. Add the following code under the `// Claim a Credential - 4.2: Retrieve Credentials` comment to
call the SDK's `retrieveCredentials` function to retrieve the offered credentials:
```ts title="/app/claim-credential.tsx"
const retrieved = await retrieveCredentials({
credentialOffer: scannedValue, // Now takes the original URL string
clientId: CLIENT_ID,
transactionCode: credentialOffer?.transactionCode
? transactionCode || undefined
: undefined,
});
if (retrieved.isErr()) {
setError(
retrieved.error.message ||
"An unexpected error occurred during offer discovery.",
);
Alert.alert(
"Error",
retrieved.error.message || "Failed to retrieve credentials.",
);
} else {
Alert.alert(
"Success",
`Retrieved ${retrieved.value.length} credential(s) successfully!`,
);
}
// Refresh the list of mobile credentials in the holder application
await getMobileCredentials();
router.replace("/");
```
Let's review where all the parameters come from:
* `credentialOffer`: Now takes the original URL string (`scannedValue`) directly instead of the
`CredentialOfferResponse` object. The SDK handles the offer discovery internally.
* `CLIENT_ID`: Defined in the `claim-credential.tsx` file. It is used by the issuer to identify
the holder application that is making a request to claim credentials.
* `transactionCode`: Optional parameter used for pre-authorized flows. When the credential offer
requires a transaction code, this should be provided by the user. Set to `undefined` for
regular authorization code flows.
3. Add the following code under the
`// Claim a Credential - Step 4.4: Add the Consent and Retrieve button` comment to display a
**Consent and Retrieve** button which call the applications' `handleConsent` function:
```ts title="/app/claim-credential.tsx"
Consent and Retrieve router.replace("/")}
>
Cancel
```
Once the `handleConsent()` function is called, it will execute the `retrieveCredentials` function.
The user will be redirected to
[authenticate](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-10.html#name-authorized-code-flow)
with the configured Authentication provider as determined by the issuer's OID4VCI workflow configuration.
This tutorial uses a demo MATTR Labs Credential offer to issue the credential.
This offer uses a workflow that doesn't actually authenticate the user before
issuing a credential, but redirects them to select the credential they wish to
issue. In production implementations this must be replaced by a proper
authentication mechanism to comply with the ISO/IEC 18013-5:2021 standard and
the OID4VCI specification.
Upon successful authentication, the user will proceed to complete the
[OID4VCI workflow](/docs/issuance/oid4vci-overview) configured by the issuer. This workflow can include
different steps based on the issuer’s configuration, but eventually the user is redirected to the
configured `REDIRECT_URI` which should be handled by your application.
In the example Credential offer used in this tutorial, the issuance workflow
immediately proceeds to claiming the credential. Check out our other issuance
guides and tutorials for creating more rich and flexible user experiences.
As the user is redirected to `REDIRECT_URI`, the issuer sends the issued mDocs to your application.
The SDK then processes the received credentials and validates them against the
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html) standard. Credentials who meet
validation rules are stored in the application.
Let's test the end-to-end flow of claiming a credential using the application you had just built.
### Step 5: Test the application [#step-5-test-the-application]
#### Authorization code flow [#authorization-code-flow]
1. Run the application.
2. Select the **Claim Credential** button.
3. Scan the following QR code:
4. Select the **Consent and retrieve Credential(s)** button.
You should see a result similar to the following:
As the user scans the QR code, the wallet retrieves and displays the offer details. The user then
provides consent to retrieving the credentials, and the wallet responds by initiating the issuance
workflow and displaying the retrieved credentials to the user.
This tutorial uses a demo MATTR Labs Credential offer to issue the credential.
This offer uses a workflow that doesn't actually authenticate the user before
issuing a credential, but redirects them to select the credential they wish to
issue. In production implementations this must be replaced by a proper
[Authentication
provider](/docs/issuance/authorization-code/authentication-provider/guide) to
comply with the ISO/IEC 18013-5:2021 standard and the OID4VCI specification.
#### Pre-authorized Code flow [#pre-authorized-code-flow]
Let's test the end-to-end flow of claiming a credential using the Pre-authorized Code flow:
1. Open the MATTR Labs [Pre-authorized Offer tool](https://tools.mattrlabs.com/issue-credential).
2. Turn on the *Transaction Code* option for the tool to generate a transaction code.
3. Select the **Generate Credential Offer** button.\
A QR code will be generated and rendered on the screen, along with a transaction code.
4. Run your application.
5. Select the **Claim Credential** button.
6. Scan the QR code.
7. Enter the transaction code retrieved from the MATTR Labs tool.
8. Select the **Consent and retrieve Credential(s)** button.
In production implementations, the transaction code is generated by the issuer
and shared with the intended holder by a separate secure channel. In this
tutorial, the transaction code is generated by the MATTR Labs tool and
displayed on the screen for demonstration purposes and to simplify testing.
Congratulations! Your application can now interact with an OID4VCI Credential offer to claim mDocs!
## Summary [#summary]
You have just used the [mDocs Holder SDKs](/docs/holding/sdk-overview) to build an application
that can claim an [mDoc](/docs/concepts/mdocs) issued via
[OID4VCI](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html), supporting
both the [Authorization Code](/docs/issuance/authorization-code/overview) and
[Pre-authorized Code](/docs/issuance/pre-authorized-code/overview) flows:
This was achieved by building the following capabilities into the application:
1. Initialize the SDK so the application can use its functions and classes.
2. Interact with a Credential offer formatted as a QR code.
3. Retrieve the offer details and present them to the user.
4. Obtain user consent and initiate the credential issuance workflow.
## What's next? [#whats-next]
* You can build additional capabilities into your new application:
* Present a claimed mDoc for verification via an
[online presentation workflow](/docs/holding/remote-presentation-tutorial) into your
new application.
* Present a claimed mDoc for verification via a
[proximity presentation workflow](/docs/holding/proximity-presentation-tutorial).
* You can check out the SDKs reference documentation for more details on the available functions and
classes:
* [iOS](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk)
* [Android](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/index.html)
* [React Native](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/index.html)
# Frequently asked questions
URL: /docs/holding/faq
Answers to common questions about credential holding with MATTR. For the concepts behind these
answers, see the [holding overview](/docs/holding).
## Frequently asked questions [#frequently-asked-questions]
### Can I add credential holding to my existing app without rebuilding it? [#can-i-add-credential-holding-to-my-existing-app-without-rebuilding-it]
Yes. The MATTR Pi Holder SDK is designed to be added to existing mobile applications. You integrate
it as a native dependency (CocoaPods/SPM for iOS, Maven/Gradle for Android, npm for React Native)
and call SDK methods from your existing codebase.
### What's the difference between MATTR Pi Holder SDK and MATTR GO Hold? [#whats-the-difference-between-mattr-pi-holder-sdk-and-mattr-go-hold]
The [MATTR Pi Holder SDK](/docs/holding/sdk-overview) is a library you embed in your own app, so
you control the full user experience. [MATTR GO Hold](/docs/holding/go-hold/getting-started) is a
standalone wallet application ready for end users. Choose the SDK for custom integration. Choose
GO Hold for rapid deployment or pilots.
### Does the SDK work offline? [#does-the-sdk-work-offline]
Yes, for proximity presentation. The SDK uses BLE for in-person credential exchange and can verify
credentials against locally cached trusted issuer certificates and status lists. Credential claiming
and remote presentation require network connectivity.
### Which credential formats are supported? [#which-credential-formats-are-supported]
The MATTR Pi Holder SDK supports [mDocs](/docs/concepts/mdocs) (aligned with ISO/IEC 18013-5) for
both claiming and presentation. This includes mobile driver's licenses (mDLs) and other mDoc-based
credentials.
### How are credentials stored securely? [#how-are-credentials-stored-securely]
The SDK uses platform-native secure storage: iOS Keychain and Android Keystore. Credential keys are
hardware-backed where available and bound to the device. Credentials cannot be extracted or cloned
to another device.
### Can my app claim credentials from any issuer? [#can-my-app-claim-credentials-from-any-issuer]
Your app can claim credentials from any issuer using the
[OID4VCI standard](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html). For
verification of issuer trust, configure your trusted issuer certificate list to include the IACA
certificates of issuers you accept.
### How do I handle credential revocation in my app? [#how-do-i-handle-credential-revocation-in-my-app]
The SDK automatically checks status lists based on the issuer's `ttl` and `exp` configuration. Your
app should handle the case where a previously valid credential's status changes. Surface this to
the user and, if appropriate, prompt them to obtain a replacement.
### Can users present credentials to verifiers not using MATTR? [#can-users-present-credentials-to-verifiers-not-using-mattr]
Yes. Because the SDK implements ISO/IEC 18013-5 (proximity) and ISO/IEC 18013-7 + OID4VP (remote),
your app can present credentials to any standards-compliant verifier regardless of their platform
provider.
### What happens if the user switches devices? [#what-happens-if-the-user-switches-devices]
Credentials are bound to the device's secure storage and cannot be transferred. Users will need to
re-claim their credentials on a new device through the original issuer's offer flow.
### How long does SDK integration typically take? [#how-long-does-sdk-integration-typically-take]
Basic credential claiming can be integrated in days. A full implementation covering claiming,
proximity presentation, and remote presentation with polished UX typically takes 2–4 weeks for an
experienced mobile team. The [quickstart guide](/docs/holding/sdk-quickstart) gets you to a first
working integration quickly.
# Credential Holding
URL: /docs/holding
Adding digital credential holding to your mobile app means your users can receive, store, and present
verifiable credentials (such as mobile driver's licenses, employee badges, or proof-of-age tokens)
without leaving your application. This overview helps product and engineering teams understand the
integration approach, key decisions, and available tooling. Start here, then follow the pages in order
or jump to the topic you need.
## Underlying platforms [#underlying-platforms]
MATTR offers two holding solutions: the [MATTR Pi Holder SDKs](/docs/holding/sdk-overview) for
embedding credential capabilities into your own mobile app, and
[MATTR GO Hold](/docs/holding/go-hold/getting-started), a ready-made wallet application you can
deploy as-is or with your own branding.
## Explore the overview [#explore-the-overview]
Work through these pages to plan your holding integration from simple to more complex concepts:
1. [SDK or ready-made wallet?](/docs/holding/sdk-or-wallet): the decision and a feature comparison.
2. [Platform requirements](/docs/holding/platform-requirements): minimum versions and frameworks.
3. [Core capabilities to integrate](/docs/holding/core-capabilities): claiming, proximity presentation, and remote presentation.
4. [Integration architecture](/docs/holding/integration-architecture): components and data flow.
5. [Trust and security considerations](/docs/holding/trust-and-security): trusted verifier and issuer management, holder authentication, and status lists.
6. [Operational tooling](/docs/holding/operational-tooling): activity log and SDK logging.
7. [Frequently asked questions](/docs/holding/faq): answers to common holding questions.
# Integration architecture
URL: /docs/holding/integration-architecture
The typical integration involves your existing mobile app, the MATTR Pi Holder SDK, and MATTR VII as
the backend platform. This page describes the components and how data flows between them.
## Integration architecture [#integration-architecture]
The typical integration involves your existing mobile app, the MATTR Pi Holder SDK, and
MATTR VII as the backend platform.
### Components [#components]
| Component | Role |
| --------------------- | -------------------------------------------------------------------------- |
| Your mobile app | User interface, business logic, navigation |
| MATTR Pi Holder SDK | Credential operations (claim, store, present) |
| MATTR VII | Issuer/verifier backend (offers, sessions, trust) |
| Device secure storage | Credential and key storage (managed by SDK via platform keychain/keystore) |
### Data flow [#data-flow]
1. **Claiming:** Your app receives an offer URI → passes it to the SDK → SDK communicates with
MATTR VII → credential is stored locally.
2. **Proximity presentation:** Verifier scans your app's QR → BLE session established → SDK handles
exchange → user consents → data shared.
3. **Remote presentation:** Verifier sends request → your app receives it → SDK processes and
validates → user consents → response sent.
## Next steps [#next-steps]
Next, review the [trust and security considerations](/docs/holding/trust-and-security) for your
integration.
# Operational tooling
URL: /docs/holding/operational-tooling
The Holder SDK provides tooling to help you build user-facing history views and to debug your
integration during development.
## Operational tooling [#operational-tooling]
### Activity log [#activity-log]
The SDK provides an [activity log](/docs/holding/sdk-operations/activity-log) that records credential
lifecycle events (claimed, presented, declined, removed). Use this to build user-facing history views
in your app.
### SDK logging [#sdk-logging]
For development and debugging, the SDK exposes
[diagnostic logging](/docs/holding/sdk-operations/sdk-logging) with configurable levels and callback
hooks. Route SDK logs to your existing app telemetry or crash reporting tools.
## Next steps [#next-steps]
Review the [frequently asked questions](/docs/holding/faq) for answers to common holding questions.
# Platform requirements
URL: /docs/holding/platform-requirements
If you are integrating the MATTR Pi Holder SDK, ensure your app meets the minimum platform
requirements before you begin.
## Platform requirements [#platform-requirements]
If you're integrating the MATTR Pi Holder SDK, ensure your app meets these minimum requirements:
| Platform | Minimum version | Language/framework |
| ------------ | --------------------------- | ----------------------- |
| iOS | iOS 15+ | Swift / Objective-C |
| Android | Android 7+ (API 24) | Kotlin |
| React Native | 0.78+ (iOS 15+, Android 7+) | JavaScript / TypeScript |
For iOS 26+, the SDK supports the Digital Credentials API, which provides a native system overlay
for credential presentation. On earlier iOS versions, the SDK uses standard redirect-based flows.
## Next steps [#next-steps]
Next, review the [core capabilities to integrate](/docs/holding/core-capabilities).
# Privacy in credential holding
URL: /docs/holding/privacy
Description: How MATTR's holder products keep credentials under the user's control, support selective disclosure, and minimize the ability to correlate usage across presentations.
Holding is the part of the credential lifecycle that sits closest to the user. The holder's wallet
is where credentials are stored, where consent is mediated, and where selective disclosure is
enforced. The privacy properties of a credential ecosystem stand or fall on what the wallet does.
This page describes how MATTR's holder products (the
[MATTR Pi Holder SDKs](/docs/holding/sdk-overview) and the
[MATTR GO](/docs/holding/go-hold/getting-started) white-label wallet) approach privacy. For the
broader picture, see [Privacy in MATTR's architecture](/docs/concepts/privacy).
## The holder is in control [#the-holder-is-in-control]
In a decentralized credential model, the holder is the party that controls the credential. The
credential is delivered to the holder once at issuance and is then stored on the holder's device.
The holder decides when to present it, to whom, and which attributes to release.
MATTR's holder products implement this principle in three concrete ways:
* **On-device storage:** Credentials are stored on the user's mobile device. Cryptographic keys
bound to the credential are protected by platform secure storage (Secure Enclave on iOS,
Keystore on Android). MATTR does not hold a server-side copy of the credential.
* **Explicit consent:** Before any credential or attribute is released to a verifier, the wallet
prompts the user to consent to the specific request. The user can see which attributes are
being requested and decline if the request is excessive.
* **Local enforcement of issuer policy:** Where the credential format and the issuer's policy
support it, the wallet enforces the issuer's rules locally. For example, the wallet can refuse
to release an attribute to a verifier that has not declared an acceptable purpose.
## Selective disclosure [#selective-disclosure]
Selective disclosure is the mechanism that lets a holder reveal only the attributes a verifier
needs, rather than the entire credential. It is one of the strongest privacy properties of the
verifiable credential model.
For a worked explanation of the concept and the business value, see
[Selective disclosure](/docs/concepts/selective-disclosure).
In practice, when a verifier sends a presentation request, the wallet:
1. Inspects the request to determine which attributes the verifier is asking for.
2. Identifies which of the holder's stored credentials can satisfy the request.
3. Shows the user the specific attributes the verifier has requested, along with the verifier's
identity where available.
4. Asks the user to consent to release those attributes.
5. Produces a cryptographic proof that reveals only the consented attributes, while still proving
the credential is signed by the issuer.
The verifier never sees the attributes that were not requested. The proof is mathematically
binding: the verifier can be confident that the disclosed attributes came from a credential signed
by the issuer, without seeing anything else.
## Minimizing correlation across presentations [#minimizing-correlation-across-presentations]
A separate concern from "what data does this verifier see" is "can multiple verifiers (or the same
verifier over time) link their interactions with the same holder." This is the unlinkability
problem, and it matters in any ecosystem where credentials are presented frequently.
MATTR's holder products approach this in several ways:
* **Credential formats that support selective disclosure:** mDocs
let the holder release only the attributes a verifier needs, reducing the surface available for
correlation.
* **Ephemeral session keys for proximity presentation:** When a credential is presented in person
using BLE (as defined in ISO/IEC 18013-5), a fresh session key is derived for
every interaction. The key is unique to that session and discarded when the session ends. See
[Proximity presentation](/docs/holding/proximity-presentation-overview) for the full engagement
flow.
* **Issuer support for short-lived credentials:** Where the use case allows, issuers can issue
credentials that expire frequently and are refreshed in the background. This reduces the value
of any single credential as a long-term correlator.
Some correlation risk is inherent to any system where a holder presents the same unique attribute
to multiple parties. If a holder presents the same driver licence number to two different
verifiers, those verifiers can correlate their records if they choose to share them. The
architecture cannot prevent this; verifier obligations and trust framework rules do that work.
## What MATTR's Holder SDKs do NOT do [#what-mattrs-holder-sdks-do-not-do]
The decentralized model gives the wallet a privileged position. It sees every credential the
holder receives and every presentation the holder makes. To preserve the architecture's privacy
properties, MATTR's holder products explicitly do not:
* Report verification activity back to MATTR or to a central server.
* Transmit credential content to MATTR's infrastructure.
* Build a server-side profile of the user across credentials and presentations.
The mobile applications use a small set of backend services for legitimate operational purposes
(license validation, application analytics about device type and OS version for compatibility and
billing, error monitoring). These services do not receive credential content and are listed
explicitly in the published sub-processor list at
[/docs/resources/terms/data-sub-processors](/docs/resources/terms/data-sub-processors).
## Practical guidance for wallet integrators [#practical-guidance-for-wallet-integrators]
If you are building a wallet using the MATTR Pi Holder SDKs or customizing MATTR GO, the
following principles support a privacy-preserving holder experience:
* Surface the verifier's identity and the specific attributes requested before asking for
consent. Users cannot exercise control over what they cannot see.
* Default consent UI to releasing the minimum required attributes. Let users opt into releasing
more only if they choose.
* Avoid logging or analytics that captures the attributes released in a presentation. Aggregate
counts are fine; per-presentation attribute logs are not.
* If your deployment supports holder analytics, make this opt-in and disclose it clearly in your
privacy notice.
* Be transparent with users about what device telemetry your wallet sends. The MATTR holder
backend collects device model, OS version, and key attestation data for compatibility and
licensing purposes. This is documented and should be reflected in the deployment's user-facing
privacy notice.
## Frequently asked questions [#frequently-asked-questions]
### Where do credentials live when a user holds them? [#where-do-credentials-live-when-a-user-holds-them]
Credentials live in the holder's wallet on their mobile device, protected by the platform's
secure storage (Secure Enclave on iOS, Keystore on Android). The credential is not stored on a
MATTR server.
### Does the issuer know when a credential is presented? [#does-the-issuer-know-when-a-credential-is-presented]
No. The cryptographic design of the credentials MATTR supports lets a verifier confirm
authenticity without contacting the issuer. The issuer is out of the verification loop and does
not observe individual presentations.
### How does selective disclosure work in practice? [#how-does-selective-disclosure-work-in-practice]
When a verifier requests information, the wallet shows the user which attributes the verifier is
asking for. The user consents to release only the requested attributes. The credential format and
cryptographic scheme produce a proof that reveals only those attributes while still proving the
credential is signed by the issuer.
### Can two verifiers correlate the same holder across presentations? [#can-two-verifiers-correlate-the-same-holder-across-presentations]
The credential formats MATTR supports are designed to limit unintended correlation, but some risk
remains if the same long-lived attribute (for example, a unique licence number) is disclosed to
multiple verifiers. Best practice is for verifiers to request only the minimum required
attributes and for issuers to support short-lived credentials and rotating identifiers where the
use case allows.
### What happens if a user loses their device? [#what-happens-if-a-user-loses-their-device]
The credential is bound to keys held in platform secure storage on the lost device. Anyone gaining
access to the device must also defeat the device's lock screen and any wallet-level authentication
to use the credential. Issuers can revoke compromised credentials via the standard revocation
flow.
## Related reading [#related-reading]
* [Privacy in MATTR's architecture](/docs/concepts/privacy)
* [Selective disclosure](/docs/concepts/selective-disclosure)
* [Decentralized trust model](/docs/concepts/decentralized-trust-model)
* [Proximity presentation](/docs/holding/proximity-presentation-overview)
* [Remote presentation](/docs/holding/remote-presentation-overview)
* [MATTR Pi Holder SDKs](/docs/holding/sdk-overview)
# mDocs proximity presentation journey pattern
URL: /docs/holding/proximity-presentation-journey-pattern
This journey pattern assumes that the wallet is presenting a credential to a verifier using MATTR verification capabilities. However, the same pattern can be applied to any ISO/IEC 18013-5 compliant verifier.
This journey pattern is used to verify an mDoc in-person via a
[proximity verification workflow](/docs/verification/in-person-overview), as per
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
## Overview [#overview]
* **Issuance channel**: In-person, supervised
* **Device/s**: Cross-device
* **Formats**: mDocs
* **Information assurance level**: High
* **Identity assurance level**: High
## Journey flow [#journey-flow]
## Architecture [#architecture]
### Establishing the connection [#establishing-the-connection]
The user opens their digital wallet app on their mobile device and selects a credential they wish to present. They then establish a secure connection with the verifier in one of the following ways:
The wallet app displays a QR code on the screen. The verifier application, which may be operated by a person or run on a self-service kiosk, scans this QR code using its device. Scanning the QR code initiates a secure Bluetooth Low Energy (BLE) connection between the two devices.
On Android devices, the BLE connection can also be initiated via NFC tap.
### Sending a presentation request [#sending-a-presentation-request]
Once the secure connection is established, the verifier application sends a presentation request to the wallet app. This request includes:
* The specific information being requested (e.g. claims or attributes from a credential).
* The identity of the verifier making the request.
* The required assurance level for the data.
* Instructions on how and where the response should be returned.
### Reviewing the presentation request [#reviewing-the-presentation-request]
The wallet app processes the presentation request and identifies any matching credentials stored on the device. It presents this information to the user, showing:
* Which credential(s) can satisfy the request.
* Exactly what data will be disclosed if the user proceeds.
* Whether selective disclosure is available for that credential, allowing the user to share only the requested claims.
* Whether the verifier is trusted, using information from a Digital Trust Service configured for the current trust network.
### Sharing the information [#sharing-the-information]
If the user consents, the wallet app:
* Creates a verifiable presentation using the selected credential and requested data.
* Cryptographically signs the presentation.
* Sends the signed presentation back to the verifier app over the BLE connection.
### Verifying the information [#verifying-the-information]
The verifier application receives the presentation and performs a series of verification checks, including:
* Validating the digital signature to confirm the data has not been tampered with.
* Checking that the credential has not been revoked or suspended, using a revocation list (if applicable).
* Verifying that the credential is currently valid, based on its “valid from” and “valid until” dates.
* Ensuring the credential was issued by a trusted issuer, based on information retrieved from a Digital Trust Service.
The issuer of the credential is not informed that the presentation has occurred. No data about the verifier, the context of use, or the interaction itself is shared with the issuer. The only interaction with the issuer is a potential call to an online revocation endpoint, if revocation checking is required.
# Proximity presentation
URL: /docs/holding/proximity-presentation-overview
Proximity presentation is a process where a credential holder presents their credential directly to
a verifier in a proximity-based interaction. This may involve showing the credential to a person
using a verifier device or to a self-service kiosk. Engagement methods include scanning a QR code, using a secure reader, or leveraging other proximity
technologies.
## mDocs [#mdocs]
### Overview [#overview]
mDocs can be presented in-person using proximity based technologies such as Bluetooth Low Energy
(BLE), and support offline verification, as defined in
[ISO/IEC 18013-5](https://www.iso.org/standard/69084.html).
### Presentation flow [#presentation-flow]
Proximity (in-person) presentation comprises two phases:
1. [**Engagement phase**](#engagement-phase): The interaction begins with the two devices attempting
to establish a secure communication channel for transferring data. In the context of mDocs, this
is between the holder’s and the verifier’s devices.
2. [**Data retrieval phase**](#data-retrieval-phase): Once a secure communication channel is
established, data can be securely requested and exchanged between the two devices. For mDocs,
this would be exchanging various data objects required for the verification workflow.
#### Engagement phase [#engagement-phase]
The engagement phase requires two mobile devices to:
* Become aware of one another: Enabling the holder’s device to explicitly share engagement
information with a verifier's device they wish to interact with.
* Establish a secure communication channel: As wireless technologies are essentially insecure,
additional security layers are required to prevent third parties from eavesdropping transactions.
The [ISO/IEC 18013-5:2021 standard](https://www.iso.org/standard/69084.html) defines this layer,
and it is quite similar to the usage of Transport Layer Security (TLS) for internet based
protocols.
##### Holder generates a QR code [#holder-generates-a-qr-code]
This phase is always initiated by the holder, who generates a QR code to be read by the verifier.
While it is true that any device can read this QR code, the assumption is that the holder will
only show the QR code to a verifier they trust and wish to share information with.
This QR code does not include any claims, credentials, or personal information. It only contains
information required to establish a secure communication channel:
* Wireless communication protocols supported by the holder.
* How the verifier can connect with the holder.
* Ephemeral session public key.
* Additional feature negotiations.
##### Verifier returns session key [#verifier-returns-session-key]
Assuming the verifier device supports the same protocol requirements, it will generate its own
ephemeral session public key, and attempt to establish a secure connection via a hand-shake
response.
The verifier response also includes an encrypted presentation request. However, for clarity
purposes this step is described as part of the data retrieval phase.
##### Secure communication established [#secure-communication-established]
As the two devices connect, a unique session transcript is created and used alongside the holder and
verifier keys to derive a unique session key that is used to encrypt any exchanged data.
This session key is unique for every session and removed when the session is terminated. Any attempt
to use the same session key in a different session would fail, even if the session is between the
same two devices.
The holder will use the session key to decrypt the message (e.g. request from the verifier) and
encrypt the responses (e.g. shared mDocs).
#### Data retrieval phase [#data-retrieval-phase]
Once a secure connection is established, the following flow takes place:
Regardless of the device engagement technology used (QR Code/NFC), all data exchanged as part of the data retrieval phase is shared securely via BLE.
##### Request [#request]
The verifier sends a presentation request. This details what type of information they wish to
verify. Note that the encrypted presentation request is actually sent alongside the verifier
ephemeral public key during the last step of the engagement phase. However, for clarity purposes
this step is described as part of the data retrieval phase.
##### Consent [#consent]
The holder receives the request and asks for the user’s consent to share the information. When
applying selective disclosure, the holder device should show the user which of their mDocs include
the required information, and enable the user to select which one to share.
##### Response [#response]
When the user agrees to share the information, a presentation is sent back to the verifier with all
of the information the user has consented to share.
##### Verification [#verification]
The verifier will check the presentation, its signature and its issuer to complete the verification
workflow with a verified/rejected conclusion.
# Learn how to build an application that can present an mDoc via a proximity workflow
URL: /docs/holding/proximity-presentation-tutorial
## Introduction [#introduction]
In this tutorial you will use the [mDocs Holder SDKs](/docs/holding/sdk-overview) to build an
application that can present a claimed [mDoc](/docs/concepts/mdocs) to a verifier that supports proximity
verification as per [ISO/IEC 18013-5](https://www.iso.org/standard/69084.html).
1. The user launches the wallet application and generates a QR code.
2. The verifier scans the QR code, connects with the wallet and requests an mDoc for verification.
3. The wallet displays matching credentials to the user and asks for consent to share them with the
verifier.
4. The verifier receives the wallet's response and verifies the provided credential.
The result will look something like this:
## Prerequisites [#prerequisites]
Before you get started, let's make sure you have everything you need.
### Prior knowledge [#prior-knowledge]
* The verification workflow described in this tutorial is based on the
[ISO/IEC 18013-5](https://www.iso.org/standard/69084.html) standard. If you are unfamiliar with
this standard, refer to the following resources for more information:
* What are [mDocs](/docs/concepts/mdocs)?
* What is [credential verification](/docs/verification)?
* Breakdown of the [proximity presentation workflow](/docs/verification/in-person-overview).
* We assume you have experience developing applications in the relevant programming languages and
frameworks (Swift for iOS, Kotlin for Android and TypeScript for React Native).
If you need to get a holding solution up and running quickly with minimal
development resources and in-house domain expertise, [talk to
us](http://mattr.global/contact-us) about our white-label [MATTR GO Hold
app](https://mattr.global/platforms/go) which might be a good fit for you.
### Prerequisite tutorial [#prerequisite-tutorial]
* You must complete the
[Claim a credential tutorial](/docs/holding/credential-claiming-tutorial) and claim the mDoc
provided in the tutorial.
* This application is used as the base for the current tutorial.
### Testing devices [#testing-devices]
As this tutorial implements a proximity presentation workflow, you will need two separate physical
devices to test the end-to-end result:
* Holder device:
* Supported iOS device to run the built application on, setup with:
* Biometric authentication.
* Bluetooth access.
* Available internet connection.
* Verifier device:
* Android/iOS device with an installed verifier application. We recommend downloading and using
the [MATTR GO Verify](/docs/verification/go-verify/getting-started) example app.
* Setup with Bluetooth access.
* Holder device:
* Supported Android device to run the built application on, setup with:
* Biometric authentication (Face recognition, fingerprint recognition).
* Bluetooth access and Bluetooth turned on.
* Available internet connection.
* [USB debugging](https://developer.android.com/studio/debug/dev-options#enable) enabled.
* Verifier device:
* Android/iOS mobile device with an installed verifier application. We recommend downloading and
using the [MATTR GO Verify](/docs/verification/go-verify/getting-started) example
app.
* Setup with Bluetooth access.
* Holder device:
* Supported iOS and/or Android device to run the built application on, setup with:
* Biometric authentication.
* Bluetooth access and Bluetooth turned on.
* Available internet connection.
* [USB debugging](https://developer.android.com/studio/debug/dev-options#enable) enabled
(Android only).
* Verifier device:
* Android/iOS device with an installed verifier application. We recommend downloading and using
the [MATTR GO Verify](/docs/verification/go-verify/getting-started) example app.
* Setup with Bluetooth access and Bluetooth turned on.
Got everything? Let's get going!
## Tutorial steps [#tutorial-steps]
To enable a user to present a stored [mDoc](/docs/concepts/mdocs) to a verifier via a
[proximity presentation workflow](/docs/verification/in-person-overview), you will build the following
capabilities into your wallet application:
1. Create a QR code for the verifier to scan and establish a secure connection.
2. Receive and handle a presentation request from the verifier.
3. Send a matching mDoc presentation to the verifier.
### Step 1: Create a QR code for the verifier to scan [#step-1-create-a-qr-code-for-the-verifier-to-scan]
The first capability you need to build is to establish a secure communication channel between the
verifier and holder devices. As defined in ISO/IEC 18130-5:2021, a
[proximity presentation workflow](/docs/verification/in-person-overview) is always initiated by the holder
(wallet user), who must create a QR code for the verifier to scan in order to initiate the
[device engagement phase](/docs/verification/in-person-overview#engagement-phase).
To achieve this, your wallet application needs a UI element for the user to interact with and
trigger device engagement by calling the SDK's `createProximityPresentationSession` method.
1. Open the project that you built as part of the
[Claim a credential tutorial](/docs/holding/credential-claiming-tutorial).
2. Open the `ContentView` file and add the following code under the
`// Proximity Presentation - Step 1.2: Create deviceEngagementString and proximityPresentationSession variables`
comment to create new variables to hold the
[device engagement string](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/proximitypresentationsession/deviceengagement)
and the
[proximity presentation session](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/proximitypresentationsession).
```swift title="ContentView"
var deviceEngagementString: String?
var proximityPresentationSession: ProximityPresentationSession?
```
3. Replace the `print` statement under the
`// Proximity Presentation - Step 1.3: Create function to create a proximity presentation session and generate QR code`
comment with the following code to call the SDK's
[`createProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/createproximitypresentationsession\(onrequestreceived:onconnected:onsessionterminated:deviceauthenticationoption:blemode:\))
method when the user selects the **Create QR Code** button:
```swift title="ContentView"
Task { @MainActor in
do {
proximityPresentationSession = try await mobileCredentialHolder.createProximityPresentationSession(
onRequestReceived: onRequestReceived(_:error:),
bleMode: .mDocPeripheralServer
)
deviceEngagementString = proximityPresentationSession?.deviceEngagement
} catch {
print(error)
}
}
```
* `bleMode`: Many modern POS terminals do not support BLE server mode. To maximize compatibility when these terminals are used as mDoc verifiers, we recommend starting the presentation session in `mDocPeripheralServer` mode. In this mode, the *holder* acts as the **BLE peripheral server**, while the *verifier* acts as the **BLE central client**.
At this stage the project won't compile because you need to update the signature of the `func onRequestReceived`. Don't worry, we'll get to that in the next step.
4. Replace the `func` statement below the
`// Proximity Presentation - Step 1.4: Update function signature` comment with the following
code to update the function's signature (don't change the function body for now):
```swift title="ContentView"
func onRequestReceived(_ mobileCredentialRequests: [(request: MobileCredentialRequest, matchedMobileCredentials: [MobileCredentialMetadata])]?, error: Error?) {
```
5. Replace the `EmptyView()` statement under the
`// Proximity Presentation - Step 1.5: Add button to generate QR code` comment and add the
following code to create a button that will generate the QR code when the user selects it:
```swift title="ContentView"
Button {
viewModel.createDeviceEngagementString()
// Navigates user to presentCredentialsView, once the string has been created.
viewModel.navigationPath.append(NavigationState.presentCredentials)
} label: {
Text("Present Credentials")
}
```
Now, when the user selects the **Create QR Code button**, the application will call the SDK's
[`createProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/createproximitypresentationsession\(onrequestreceived:onconnected:onsessionterminated:deviceauthenticationoption:blemode:\))
method, which returns a
[`ProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/proximitypresentationsession)
instance that includes a `deviceEngagement` string in `base64` format:
```
"mdoc:owBjMS4wAYIB2BhYS6QBAiABIVgghaBYJe7KSqcEolhmnIJaYJ2AIevkKbEy5xP7tkwlqAwiWCAMGCGe6uFI2hKeghb59h_K4hPV-Ldq6vnaxsRiySMH9gKBgwIBowD0AfULUKRoj0ZH60Qco-m0k97qRSQ"
```
The `deviceEngagement` string is always prefixed with `mdoc:` and contains the information required
to establish a secure connection between the two devices, including:
* Wireless communication protocols supported by the holder.
* How the verifier can connect with the holder.
* Ephemeral public key which is unique to the current session.
* Additional feature negotiations.
Your app needs to convert this `deviceEngagement` string into a QR code and display it in the wallet
UI for the verifier to scan.
6. Replace the `return nil` statement under the
`// Proximity Presentation - Step 1.6: Generate QR code` comment with the following code to
retrieve data from the `deviceEngagement` string and convert it into a QR code:
```swift title="ContentView"
guard let filter = CIFilter(name: "CIQRCodeGenerator") else { return nil }
filter.setValue(data, forKey: "inputMessage")
guard let ciimage = filter.outputImage else { return nil }
let transform = CGAffineTransform(scaleX: 10, y: 10)
let scaledCIImage = ciimage.transformed(by: transform)
let uiimage = UIImage(ciImage: scaledCIImage)
return uiimage.pngData()
```
7. Replace the `EmptyView()` statement under the
`// Proximity Presentation - Step 1.7: Create QR code view` comment with the following code to
generate a QR Code and display it to the user:
```swift title="ContentView"
VStack {
Text("Scan to establish device engagement session")
.font(.title3)
Spacer()
if let imageData = generateQRCode(data: viewModel.deviceEngagementString?.data(using: .utf8) ?? Data()),
let image = UIImage(data: imageData) {
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fit)
}
Spacer()
}
```
8. [Run](https://developer.apple.com/documentation/xcode/running-your-app-in-simulator-or-on-a-device)
the app and select the **Present Credentials** button. You should see a result similar to the
following:
* As the user selects the **Present Credentials** button, they are navigated to the new view, and a
new proximity presentation session is created.
* Once the session is created, the application generates and displays a QR code that can be scanned
by a verifier device to establish a secure proximity communication channel over Bluetooth.
* Once the QR code is displayed, the
[`createProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/proximitypresentationsession)
function enters a listening state, ready to establish a Bluetooth connection with a verifier
application that scans the QR Code.
* When a verifier application scans the QR code, the devices will automatically exchange public keys
to establish a secure communication channel, enabling the verifier to send a presentation request,
which details:
* What credentials are required.
* What specific claims are required from these credentials.
1. In your [Claim a tutorial](/docs/holding/credential-claiming-tutorial)
application project, create a new file named `PresentationQrScreen.kt` and add the following
code:
```kotlin title="PresentationQrScreen.kt"
package com.example.holdertutorial
import android.app.Activity
import android.graphics.Bitmap
import android.graphics.Color
import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.unit.IntSize
import androidx.navigation.NavController
import com.google.zxing.BarcodeFormat
import com.google.zxing.EncodeHintType
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import com.journeyapps.barcodescanner.BarcodeEncoder
import global.mattr.mobilecredential.holder.MobileCredentialHolder
import global.mattr.mobilecredential.holder.ProximityPresentationSession
import global.mattr.mobilecredential.holder.datatransport.bluetooth.BleMode
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
fun PresentationQrScreen(activity: Activity, navController: NavController) {
var containerSize by remember { mutableStateOf(IntSize.Zero) }
var session: ProximityPresentationSession? by remember { mutableStateOf(null) }
var qrCode: Bitmap? by remember { mutableStateOf(null) }
LaunchedEffect(Unit) {
// Step 1.4: Create a Proximity presentation session
}
LaunchedEffect(session, containerSize) {
// Step 1.6: Generate a QR code
}
Box(
modifier = Modifier
.aspectRatio(1f)
.onSizeChanged { containerSize = it }
) {
qrCode?.let {
Image(
bitmap = it.asImageBitmap(),
contentDescription = "A QR Code",
modifier = Modifier.fillMaxSize()
)
}
}
}
// Step 1.5: Create function to generate QR Code from String
```
We will copy and paste different code snippets into specific locations in this codebase to
achieve the different functionalities. These locations are indicated by comments that reference
both the section and the step.
We recommend copying and pasting the comment text (e.g. `// Proximity Presentation - Step 1.2: Add "QR" screen call`) to
easily locate it in the code.
2. Return to your `MainActivity` file and add the following code under the
`// Proximity Presentation - Step 1.2: Add "QR Presentation" screen call` comment to connect the
created composable to the navigation graph:
```kotlin title="MainActivity.kt"
PresentationQrScreen(this@MainActivity, navController)
```
3. Still in the `MainActivity` file file, add the following code under the
`// Proximity Presentation - Step 1.3: Add button for starting the credentials presentation workflow`
comment to add a button that navigates the user to the `PresentationQrScreen`:
```kotlin title="MainActivity.kt"
Button(onClick = { navController.navigate("presentationQr") }, Modifier.fillMaxWidth()) {
Text("Present Credentials")
}
```
4. Open the `PresentationQrScreen.kt` file and add the following code under the
`// Step 1.4: Create a Proximity presentation session` comment to call the
[`createProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/create-proximity-presentation-session.html)
function when the screen is loaded:
```kotlin title="PresentationQrScreen.kt"
session = MobileCredentialHolder.getInstance().createProximityPresentationSession(
activity,
bleMode = BleMode.MDocPeripheralServer,
onRequestReceived = { _, requests, e ->
// Step 2.2: Handle the presentation request
}
)
```
* `bleMode`: Many modern POS terminals do not support BLE server mode. To maximize compatibility when these terminals are used as mDoc verifiers, we recommend starting the presentation session in `mDocPeripheralServer` mode. In this mode, the *holder* acts as the **BLE peripheral server**, while the *verifier* acts as the **BLE central client**.
Now, when the `PresentationQrScreen` is loaded, the application will call the SDK's
[`createProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/create-proximity-presentation-session.html)
function, which returns a
[`ProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-proximity-presentation-session/index.html)
instance that includes a `deviceEngagement` string in `base64` format:
```
"mdoc:owBjMS4wAYIB2BhYS6QBAiABIVgghaBYJe7KSqcEolhmnIJaYJ2AIevkKbEy5xP7tkwlqAwiWCAMGCGe6uFI2hKeghb59h_K4hPV-Ldq6vnaxsRiySMH9gKBgwIBowD0AfULUKRoj0ZH60Qco-m0k97qRSQ"
```
This `deviceEngagement` string is always prefixed with `mdoc:` and contains the information required
to establish a secure connection between the holder and verifier devices, including:
* Wireless communication protocols supported by the holder.
* How the verifier can connect with the holder.
* Ephemeral public key which is unique to the current session.
* Additional feature negotiations.
Your application needs to convert this `deviceEngagement` string into a QR code and display it in
the `PresentationQrScreen` for the verifier to scan.
5. Add the following code under the `// Step 1.5: Create function to generate QR Code from String`
comment to add a function that converts a `String` to a QR code rendered as a `Bitmap` image:
```kotlin title="PresentationQrScreen.kt"
private fun String.toQrCode(size: IntSize): Bitmap? {
if (this.isEmpty() || size == IntSize.Zero) return null
val (width, height) = size
return Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565).apply {
val hints = mapOf(EncodeHintType.ERROR_CORRECTION to ErrorCorrectionLevel.M)
val encoded = BarcodeEncoder()
.encode(this@toQrCode, BarcodeFormat.QR_CODE, width, height, hints)
for (x in 0 until width) {
for (y in 0 until height) {
setPixel(x, y, if (encoded[x, y]) Color.BLACK else Color.WHITE)
}
}
}
}
```
To generate the QR code, you need the `ProximityPresentationSession` to be established by the SDK
and the container size to be calculated.
6. Add the following code under the `// Step 1.6: Generate a QR code` comment to call the new
`toQrCode` function and generate the QR code when the `session` or `containerSize` state changes:
```kotlin title="PresentationQrScreen.kt"
qrCode = session?.deviceEngagement?.toQrCode(containerSize)
```
7. [Run](https://developer.android.com/studio/run#basic-build-run) the app, and select the **Present
Credentials** button. You should see a result similar to the following:
* As the user selects the **Present Credentials** button, they are navigated to the new
`PresentationQrScreen`, and a new proximity presentation session is created.
* Once the session is created, the application generates and displays a QR code that can be scanned
by a verifier device to establish a secure proximity communication channel over Bluetooth.
* Once the QR code is displayed, the
[`createProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/create-proximity-presentation-session.html?query=suspend%20fun%20createProximityPresentationSession\(activity:%20Activity,%20deviceAuthenticationOption:%20DeviceAuthenticationOption%20=%20DeviceAuthenticationOption.Signature,%20bleMode:%20BleMode%20=%20BleMode.MDocClientCentral,%20onRequestReceived:%20ProximityPresentationSession.OnRequestReceived,%20onConnected:%20ProximityPresentationSession.OnConnected?%20=%20null,%20onSessionTerminated:%20ProximityPresentationSession.OnSessionTerminated?%20=%20null\):%20ProximityPresentationSession)
function enters a listening state, ready to establish a Bluetooth connection with a verifier
application that scans the QR Code.
* When a verifier application scans the QR code, the devices will automatically exchange public keys
to establish a secure communication channel, enabling the verifier to send a presentation request,
which details:
* What credentials are required.
* What specific claims are required from these credentials.
1. In your project's `app` directory, create a new file named `proximity-presentation.tsx` and add
the following scaffolding code to create a new screen that will display the generated QR code:
```ts title="app/proximity-presentation.tsx"
// Step 2.2: Import Credential selector component
import { useHolder } from "@/providers/HolderProvider";
import {
type CreateProximityPresentationSessionOptions,
type PresentationSessionSuccessRequest,
createProximityPresentationSession,
sendProximityPresentationResponse,
terminateProximityPresentationSession,
} from "@mattrglobal/mobile-credential-holder-react-native";
import { useRouter } from "expo-router";
import React, { useState, useCallback, useEffect } from "react";
import {
Alert,
StyleSheet,
Text,
TouchableOpacity,
View,
} from "react-native";
import QRCode from "react-native-qrcode-svg";
export default function ProximityPresentation() {
const router = useRouter();
const { isHolderInitialized } = useHolder();
const [error, setError] = useState(null);
const [deviceEngagement, setDeviceEngagement] = useState(
null,
);
const [requests, setRequests] = useState<
PresentationSessionSuccessRequest["request"]
>([]);
const [selectedCredentialIds, setSelectedCredentialIds] = useState<
string[]
>([]);
const navigateToIndex = useCallback(() => {
router.push("/");
}, [router]);
const resetState = useCallback(() => {
setDeviceEngagement(null);
setRequests([]);
setSelectedCredentialIds([]);
}, []);
const handleError = useCallback((message: string) => {
setError(message);
console.error(message);
}, []);
// Step 2.3: Add handleToggleSelection function
if (!isHolderInitialized) {
return (
Holder instance not found. Please restart the app and try again.
);
}
// Step 1.2: Add handleStartSession function
// Step 1.6: Add terminateSession function
// Step 3.1: Add handleSendResponse function
return (
{error && {error}}
{/* Step 1.3: Add fallback UI */}
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 16, paddingBottom: 32 },
errorText: { color: "red", marginBottom: 10 },
infoText: { marginBottom: 10 },
qrContainer: { alignItems: "center", marginVertical: 20 },
button: {
backgroundColor: "#007AFF",
paddingVertical: 12,
paddingHorizontal: 20,
borderRadius: 8,
alignItems: "center",
marginTop: 20,
},
buttonDanger: {
backgroundColor: "#FF3B30",
},
buttonText: {
color: "white",
fontSize: 16,
fontWeight: "600",
},
});
```
This will serve as the basic structure for this capability. We will copy and
paste different code snippets into specific locations in this codebase to
achieve the different functionalities. These locations are indicated by
comments that reference both the section and the step. We recommend copying
and pasting the comment text (e.g. `// Proximity Presentation - Step 1.2: Add
Proximity Presentation button`) to easily locate it in the code.
2. Add the following code under the `// Step 1.2: Add handleStartSession function` comment to create
a new `handleStartSession` function that calls the SDK's
[`createProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/createProximityPresentationSession.html)
function:
```ts title="app/proximity-presentation.tsx"
const handleStartSession = useCallback(async () => {
try {
const options: CreateProximityPresentationSessionOptions = {
bleMode: "MDocPeripheralServer",
onRequestReceived: (data) => {
if ("error" in data) {
handleError(`Request received error: ${data.error.message}`);
return;
}
setRequests(data.request);
},
onSessionTerminated: (error) => {
if (error) {
console.log(`Session terminated with error: ${error.message}`);
}
resetState();
navigateToIndex();
},
};
const result = await createProximityPresentationSession(options);
if (result.isErr()) {
throw new Error(
`Error creating proximity session: ${result.error.message || result.error.type}`,
);
}
setDeviceEngagement(result.value.deviceEngagement);
} catch (err: unknown) {
handleError(err instanceof Error ? err.message : String(err));
}
}, [handleError, resetState, navigateToIndex]);
// Automatically start session when component mounts
useEffect(() => {
handleStartSession();
}, [handleStartSession]);
```
* `bleMode`: Many modern POS terminals do not support BLE server mode. To maximize compatibility when these terminals are used as mDoc verifiers, we recommend starting the presentation session in `MDocPeripheralServer` mode. In this mode, the *holder* acts as the **BLE peripheral server**, while the *verifier* acts as the **BLE central client**.
3. Add the following code under the `{/* Step 1.3: Add fallback UI */}` comment to display a message
to the user while the proximity session is being established:
```ts title="app/proximity-presentation.tsx"
{
!deviceEngagement ? (
<>
Waiting for session to establish...
>
) : (
<>{/* Step 1.4: Display QR code */}>
);
}
```
Now, when the user navigates to the **Proximity Presentation** screen, the `handleStartSession`
function is called and creates a new proximity presentation session by calling the SDK's
[`createProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/createProximityPresentationSession.html)
function. This SDK function returns a
[`ProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/types/ProximityPresentationSession.html)
instance that includes a `deviceEngagement` string in `Base64` format:
```
"mdoc:owBjMS4wAYIB2BhYS6QBAiABIVgghaBYJe7KSqcEolhmnIJaYJ2AIevkKbEy5xP7tkwlqAwiWCAMGCGe6uFI2hKeghb59h_K4hPV-Ldq6vnaxsRiySMH9gKBgwIBowD0AfULUKRoj0ZH60Qco-m0k97qRSQ"
```
The `deviceEngagement` string is always prefixed with `mdoc:` and contains the information
required to establish a secure connection between the two devices, including:
* Wireless communication protocols supported by the holder.
* How the verifier can connect with the holder.
* Ephemeral public key which is unique to the current session.
* Additional feature negotiations.
Your app needs to convert this `deviceEngagement` string into a QR code and display it so that
the verifier can scan it.
4. Add the following code under the `{/* Step 1.4: Display QR code */}` comment to use the
`deviceEngagement` variable to generate a QR code and display it:
```ts title="app/proximity-presentation.tsx"
<>
A proximity session is active.
{requests.length === 0 ? (
{/* Step 1.7: Add Terminate session button */}
) : (
<>
{/* Step 2.4: Display request details */}
{/* Step 3.2: Send response */}
>
)}
>
```
5. Open the `index.tsx` file in the `app` directory and add the following code under the
`{/* Proximity Presentation - Step 1.5: Add Proximity Presentation button */}` comment to add a
new button to navigate to the new `proximity-presentation` screen created in the previous steps:
```ts title="app/index.tsx"
router.replace("/proximity-presentation")}
>
Present Credential
```
Proximity verification workflows require the use of Bluetooth to establish a secure communication
channel with a verifier device. To enable this feature for iOS applications, you need to adjust the
app configuration to include the necessary permissions.
To properly manage proximity sessions, we will create two functions that allow the application to
gracefully handle scenarios where users navigate away from the screen before completing the
verification process.
6. Add the following code under the
`{/* Proximity Presentation - Step 1.7: Add terminateSession function */}` comment to create the
`terminateSession` and `handleTerminateSession` functions:
```ts title="app/proximity-presentation.tsx"
const terminateSession = useCallback(async () => {
try {
await terminateProximityPresentationSession();
resetState();
navigateToIndex();
} catch (err: unknown) {
const message = err instanceof Error ? err.message : String(err);
handleError(`Failed to terminate session: ${message}`);
}
}, [resetState, handleError, navigateToIndex]);
const handleTerminateSession = useCallback(async () => {
await terminateSession();
}, [terminateSession]);
useEffect(() => {
return () => {
if (deviceEngagement) {
terminateSession();
}
};
}, [deviceEngagement, terminateSession]);
```
7. Add the following code under the
`{/* Proximity Presentation - Step 1.7: Add Terminate session button */}` comment to create a
button that will call the `handleTerminateSession` function when selected:
```ts title="app/proximity-presentation.tsx"
Terminate Session
```
The `handleTerminateSession` function is called when the user selects the **Terminate Session**
button, and it displays an alert to confirm that the session has been terminated.
The `useEffect` hook acts as a cleanup function when the component is unmounted or if the user
navigates away from the screen. If a proximity session is active, it calls the
`terminateSession` function to end the session.
The `terminateSession` function calls the SDK's `terminateProximityPresentationSession` method
to end the current proximity presentation session and reset the application state.
8. Run the app and select the **Present Credential** button. You should see a result similar to
the following:
* As the user selects the **Present Credential** button a new proximity presentation session is
created.
* Once the session is created, the application retrieves the `deviceEngagement` string and uses it
to generate and display a QR code that can be scanned by a verifier device to establish a secure
proximity communication channel over Bluetooth.
* Once the QR code is displayed, the
[`createProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/createProximityPresentationSession.html)
function enters a listening state, ready to establish a Bluetooth connection with a verifier
application that scans the QR Code.
* When a verifier application scans the QR code, the devices will automatically exchange public keys
to establish a secure communication channel, enabling the verifier to send a presentation request,
which details:
* What credentials are required.
* What specific claims are required from these credentials.
We will implement the logic that handles this presentation request in the next step.
### Step 2: Handle the presentation request [#step-2-handle-the-presentation-request]
The `createProximityPresentationSession` function can handle three types of events:
* `onConnected`: When a secure connection is established.
* `onSessionTerminated`: When a secure connection is terminated for whatever reason.
* `onRequestReceived`: When a presentation request is received from the verifier.
`onConnected` and `onSessionTerminated` are optional events and will not be
implemented in this tutorial. You can find more information about these events
in the reference documentation for the
[iOS](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk),
[Android](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/index.html)
and [React
Native](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/index.html)
SDKs.
When the SDK receives a presentation request from a verifier, an `onRequestReceived` event is
triggered. The SDK then checks its credential storage for any credentials that match the information
defined in this request.
The application then needs to:
* Present these matching credentials to the user.
* Present what claims will be shared with the verifier.
* Provide a UI element for the user to consent sharing this information with the verifier.
The following step is also included in the [Online presentation
tutorial](/docs/holding/remote-presentation-tutorial). If you had already
completed this tutorial you may skip to step 2.
1. Add the following code under the
`// Proximity and Online Presentation: Create variables for credential presentations` comment to
create the following variables:
```swift title="ContentView"
var matchedCredentials: [MobileCredential] = []
var matchedMetadata: [MobileCredentialMetadata] = []
var credentialRequest: [MobileCredentialRequest] = []
```
* `matchedCredentials` : Holds stored credentials that match the credential request.
* `matchedMetadata` : Holds metadata of credentials that match the credential request.
* `credentialRequest`: Holds the credentials that were requested for verification.
Once the application receives a credential request from a verifier (`onRequestReceived` event), the
[createProximityPresentationSession](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/createproximitypresentationsession\(onrequestreceived:onconnected:onsessionterminated:deviceauthenticationoption:blemode:\))
function will return a `mobileCredentialRequests` array that includes pairs of credentials requested
by the verifier
([MobileCredentialRequest](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialrequest))
and the metadata of stored credentials that match the requested information
([MobileCredentialMetadata](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialmetadata)).
2. Replace the `print` statements under the
`// Proximity Presentation - Step 2.2: Store credential requests and matched credentials` comment
with the following code to store the values from the `mobileCredentialRequests` array in the
`matchedMetadata` and `credentialRequest` variables:
```swift title="ContentView"
Task { @MainActor in
matchedMetadata = mobileCredentialRequests?
.flatMap { $0.matchedMobileCredentials }
.compactMap { $0 } ?? []
credentialRequest = mobileCredentialRequests?
.compactMap { $0.request }
.compactMap { $0 } ?? []
// Navigate to presentation view if there are no errors
if error == nil {
navigationPath.append(NavigationState.proximityPresentation)
} else {
print(error!)
}
}
```
The following two steps are also included in the [Online presentation
tutorial](/docs/holding/remote-presentation-tutorial). If you had already
completed this tutorial you may skip to step 5.
3. Replace the `print` statement under the
`// Proximity and Online Presentation: Retrieve a credential from storage` comment with the
following code create a function that uses the SDK's
[getCredential](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/getcredential\(credentialid:fetchupdatedstatuslist:\))
method to retrieve a credential from the application storage:
```swift title="ContentView"
Task {
do {
let credential = try await mobileCredentialHolder.getCredential(credentialId: id)
matchedCredentials.append(credential)
} catch {
print(error)
}
}
```
The
[MobileCredentialMetadata](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialmetadata)
object does not include the values of claims included in the credential. To display these
values, the above function calls the SDK's
[getCredential](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/getcredential\(credentialid:fetchupdatedstatuslist:\))
method with the `id` property of the
[MobileCredentialMetadata](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialmetadata).
4. Create a new file called `PresentCredentialsView.swift` and paste the following code to create a
view to display credential requests and matching credentials stored in the application:
```swift title="PresentCredentialsView"
import MobileCredentialHolderSDK
import SwiftUI
struct PresentCredentialsView: View {
var viewModel: PresentCredentialsViewModel
@State var selectedID: String?
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 20) {
Text("Requested Documents")
.font(.headline)
.padding(.leading)
ForEach(viewModel.requestedDocuments, id: \.docType) { requestedDocument in
DocumentView(viewModel: DocumentViewModel(from: requestedDocument))
}
Text("Matched Credentials")
.font(.headline)
.padding(.leading)
ForEach(viewModel.matchedMetadata, id: \.id) { matchedMetadata in
VStack(alignment: .leading, spacing: 10) {
if let matchedCredential = viewModel.matchedMobileCredential(id: matchedMetadata.id) {
DocumentView(viewModel: DocumentViewModel(from: matchedCredential))
.padding(.vertical)
.background(selectedID == matchedMetadata.id ? Color.blue.opacity(0.2) : Color.clear)
.onTapGesture {
guard selectedID != matchedMetadata.id else {
selectedID = nil
return
}
selectedID = matchedMetadata.id
}
Button("Hide claim values") {
viewModel.matchedCredentials.removeAll(where: { $0.id == matchedMetadata.id })
}
.frame(maxWidth: .infinity, alignment: .center)
} else {
DocumentView(viewModel: DocumentViewModel(from: matchedMetadata))
.padding(.vertical)
.background(selectedID == matchedMetadata.id ? Color.blue.opacity(0.2) : Color.clear)
.onTapGesture {
guard selectedID != matchedMetadata.id else {
selectedID = nil
return
}
selectedID = matchedMetadata.id
}
Button("Show claim values") {
viewModel.getCredentialAction(matchedMetadata.id)
}
.frame(maxWidth: .infinity, alignment: .center)
}
}
}
}
}
if selectedID != nil {
Button("Send Response") {
viewModel.sendCredentialAction(selectedID!)
}
.buttonStyle(.borderedProminent)
.clipShape(Capsule())
.frame(maxWidth: .infinity, alignment: .center)
}
}
}
// MARK: PresentCredentialsViewModel
class PresentCredentialsViewModel {
@Binding var requestedDocuments: [MobileCredentialRequest]
@Binding var matchedCredentials: [MobileCredential]
@Binding var matchedMetadata: [MobileCredentialMetadata]
var getCredentialAction: (String) -> Void
var sendCredentialAction: (String) -> Void
init(
requestedDocuments: Binding<[MobileCredentialRequest]>,
matchedCredentials: Binding<[MobileCredential]>,
matchedMetadata: Binding<[MobileCredentialMetadata]>,
sendCredentialAction: @escaping (String) -> Void,
getCredentialAction: @escaping (String) -> Void
) {
self._requestedDocuments = requestedDocuments
self._matchedCredentials = matchedCredentials
self._matchedMetadata = matchedMetadata
self.sendCredentialAction = sendCredentialAction
self.getCredentialAction = getCredentialAction
}
func matchedMobileCredential(id: String) -> MobileCredential? {
matchedCredentials.first(where: { $0.id == id })
}
}
```
The `PresentCredentialsView` view is used to:
* Display requested information.
* Display stored credentials that include the requested information.
* Enable the user to provide consent to sharing the requested information with the verifier.
The `PresentCredentialsViewModel` object is used to reference values from a credential request.
It takes two closures in its initializer:
* `getCredentialAction: (String) -> Void` is used to display claim values.
* `sendCredentialAction: (String) -> Void` is used to send a credential response to the verifier
once the user selected a credential and provided consent by selecting the **Send Response**
button.
5. Return to the `ContentView` file and replace the `EmptyView()` statement under the
`// Proximity Presentation - Step 2.5: Display proximity presentation view` comment with the new
view that you created:
```swift title="ContentView"
PresentCredentialsView(
viewModel: PresentCredentialsViewModel(
requestedDocuments: $viewModel.credentialRequest,
matchedCredentials: $viewModel.matchedCredentials,
matchedMetadata: $viewModel.matchedMetadata,
sendCredentialAction: viewModel.sendProximityPresentationResponse(id:),
getCredentialAction: viewModel.getCredential(id:)
)
)
```
6. [Run](https://developer.apple.com/documentation/xcode/running-your-app-in-simulator-or-on-a-device)
the app, select the **Present Credentials** button and then select the **Create QR code**
button. Next, use your testing verifier app to scan the presented QR code and send a
presentation request. You should see a result similar to the following:
As the user selects the **Present Credentials** button, the wallet generates and displays a QR code.
When a compliant verifier app scans the QR code, a secure communication channel is established via
Bluetooth. The verifier then sends a presentation request, which is displayed to the user on their
digital wallet, alongside any credentials they have stored in their wallet that match the request.
When the user selects the **Show claim values** button, the SDK retrieves the corresponding
credential and the application displays its claim values.
When the user selects a credential, it is highlighted and the **Send Response** button appears at
the bottom of the screen.
1. In your `MainActivity` file, add the following code under the
`// Step 2.1: Add proximity presentation request variable` comment to create a new variable that
will hold the presentation request information and share it between the screens:
```kotlin title="MainActivity.kt"
var proximityPresentationRequest: ProximityPresentationSession.CredentialRequestInfo? = null
```
2. Open the `PresentationQrScreen.kt` file and add the following code under the
`// Step 2.2: Handle the presentation request` comment to navigate to a new screen when an
`onRequestReceived` event is received.
```kotlin title="PresentationQrScreen.kt"
if (e == null && !requests.isNullOrEmpty()) {
// Using only the first request for simplicity
SharedData.proximityPresentationRequest = requests.first()
withContext(Dispatchers.Main) {
navController.navigate("presentationSelectCredentials")
}
} else {
val msg = "Error while retrieving the request"
withContext(Dispatchers.Main) {
Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show()
}
}
```
3. Create a new file named `PresentationSelectCredentialsScreen.kt` and add the following code to
create the new screen which displays matching credentials and enables the user to select which
credential to share:
```kotlin title="PresentationSelectCredentialsScreen.kt"
package com.example.holdertutorial
import android.app.Activity
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import global.mattr.mobilecredential.holder.deviceretrieval.devicerequest.DataElements
import global.mattr.mobilecredential.holder.deviceretrieval.deviceresponse.NameSpace
import global.mattr.mobilecredential.holder.dto.MobileCredential
import global.mattr.mobilecredential.holder.dto.MobileCredentialMetaData
import global.mattr.mobilecredential.holder.MobileCredentialHolder
import kotlinx.coroutines.launch
@Composable
fun PresentationSelectCredentialsScreen(activity: Activity) {
val request = SharedData.proximityPresentationRequest ?: return
var matchedCredentials by remember { mutableStateOf(request.matchedCredentials) }
var selectedCredentialId by remember { mutableStateOf(matchedCredentials.first().id) }
val coroutineScope = rememberCoroutineScope()
Column(Modifier.verticalScroll(rememberScrollState())) {
Text("REQUESTED DATA", style = MaterialTheme.typography.titleLarge)
Card(Modifier.padding(vertical = 8.dp)) {
Document(request.request.docType, request.request.namespaces.value.toUi())
}
Spacer(Modifier.padding(12.dp))
Text("MATCHED CREDENTIALS", style = MaterialTheme.typography.titleLarge)
Spacer(Modifier.padding(6.dp))
matchedCredentials.forEach { matchedCredential ->
// Step 2.5: Display matching credentials and claims
}
// Step 3.2: Send response
}
}
// Step 2.4: Create function to add values to claims
private fun Map.toUi() = mapValues { (_, dataElements) ->
dataElements.value.keys.toSet()
}
// Step 3.1: Create function to send the credential response
```
This code is very similar to the one used in the in the
`OnlinePresentationScreen.kt` file in the [Online
Presentation](/docs/holding/remote-presentation-tutorial) tutorial, to avoid
creating dependencies between the tutorials. In your own project you can use
the same components for both presentation workflows.
The application retrieves information from the SDK's `ProximityPresentationSession` object and
displays it to the user:
* Credentials and claims included in the verification request are retrieved from the `docType` and
`nameSpaces.value` properties and displayed in the *"REQUESTED INFORMATION"* area.
* Available credentials that match the requested information are retrieved from the
`matchedCredentials` property and displayed in the *"MATCHING CREDENTIALS"* area.
4. Add the following code under the `// Step 2.4: Create function to add values to claims` comment
to create a new function that will enable displaying the values of the claims the user is about
to share.
```kotlin title="PresentationSelectCredentialsScreen.kt"
private fun List.withClaimValues(
from: MobileCredential
): List = map { credential ->
if (credential.id != from.id) {
credential
} else {
credential.copy(
claims = credential.claims.mapValues { (namespace, claims) ->
claims.map { claim ->
val claimValue = from.claims[namespace]?.get(claim)
claimValue?.let { "$claim: ${it.toUiString()}" } ?: claim
}.toSet()
}
)
}
}
```
5. Add the following code under the `// Step 2.5: Display matching credentials and claims` comment
to display to the user what credentials and claims they are about to share with the verifier, as
well as a button that enables the user to display the value of those claims:
```kotlin title="PresentationSelectCredentialsScreen.kt"
val borderWidth = if (matchedCredential.id == selectedCredentialId) 4.dp else 0.dp
Column(
Modifier
.clickable { selectedCredentialId = matchedCredential.id }
.border(borderWidth, Color.Blue, RoundedCornerShape(16.dp))
.padding(8.dp)
) {
Card(Modifier.fillMaxWidth()) {
Document(matchedCredential.docType, matchedCredential.claims)
}
Button(
onClick = {
val credentialWithValues = MobileCredentialHolder.getInstance()
.getCredential(matchedCredential.id, fetchUpdatedStatusList = false)
matchedCredentials =
matchedCredentials.withClaimValues(from = credentialWithValues)
},
Modifier.fillMaxWidth()
) { Text("Show Values") }
}
Spacer(Modifier.padding(12.dp))
```
6. Back in the `MainActivity` file, add the following code under the
`// Proximity Presentation - Step 2.6: Add "Select Credential" screen call` comment to connect
the created composable to the navigation graph:
```kotlin title="MainActivity.kt"
PresentationSelectCredentialsScreen(this@MainActivity)
```
7. [Run](https://developer.android.com/studio/run#basic-build-run) the app, and select the **Present
Credentials** button. Next, use your testing verifier app to scan the presented QR code and send
a presentation request. You should see a result similar to the following:
* As the user selects the **Present Credentials** button, they are navigated to the new
`PresentCredentialsView` where the application generates and displays a QR code.
* When a compliant verifier app scans the QR code, a secure communication channel is established via
Bluetooth.
* The verifier then sends a presentation request, which is displayed to the user alongside any
credentials they have stored that match the request.
The following step is also included in the [Online presentation
tutorial](/docs/holding/remote-presentation-tutorial#handle-a-presentation-request).
If you had already completed this tutorial and created the
`RequestCredentialSelector.tsx` component, you may skip to step 2.2.
1. In the `app/components` directory, create a file named `RequestCredentialSelector.tsx` and add
the following code:
```ts title="app/components/RequestCredentialSelector.tsx"
import type {
MobileCredentialMetadata,
OnlinePresentationSession,
} from "@mattrglobal/mobile-credential-holder-react-native";
import type React from "react";
import {
FlatList,
type ListRenderItem,
StyleSheet,
Text,
TouchableOpacity,
View,
} from "react-native";
type RequestCredentialSelectorProps = {
requests: PresentationSessionSuccessRequest["request"];
selectedCredentialIds: string[];
onToggleSelection: (credentialId: string) => void;
};
type RequestItem = PresentationSessionSuccessRequest["request"][number];
/**
* Component that renders a list of credential requests and their matched credentials.
*
* @param props - The component props.
* @param props.requests - The list of credential requests.
* @param props.selectedCredentialIds - The list of selected credential IDs.
* @param props.onToggleSelection - Callback function to toggle the selection of a credential.
* @returns The rendered component.
*/
export default function RequestCredentialSelector({
requests,
selectedCredentialIds,
onToggleSelection,
}: RequestCredentialSelectorProps) {
const renderCredential: ListRenderItem = ({
item: cred,
}) => {
const isSelected = selectedCredentialIds.includes(cred.id);
return (
onToggleSelection(cred.id)}
>
{isSelected && }
{cred.branding?.name ?? "Credential"} ({cred.id})
);
};
const renderRequest: ListRenderItem = ({ item }) => (
Request Details
{typeof item.request === "object"
? JSON.stringify(item.request, null, 2)
: item.request}
Matched Credentials: cred.id}
renderItem={renderCredential}
style={styles.credentialsList}
contentContainerStyle={styles.credentialsListContent}
/>
);
return (
idx.toString()}
renderItem={renderRequest}
style={styles.requestsList}
contentContainerStyle={styles.requestsListContent}
/>
);
}
const styles = StyleSheet.create({
requestsList: {
flex: 1,
},
requestsListContent: {
paddingBottom: 10,
},
requestContainer: {
backgroundColor: "#f0f0f0",
padding: 10,
borderRadius: 8,
marginBottom: 15,
},
requestInfo: {
fontStyle: "italic",
fontSize: 12,
marginBottom: 10,
},
label: {
fontWeight: "bold",
fontSize: 14,
textTransform: "uppercase",
marginBottom: 5,
},
credentialsList: {
maxHeight: 200,
},
credentialsListContent: {
paddingBottom: 10,
},
credentialItem: {
flexDirection: "row",
alignItems: "center",
marginBottom: 8,
paddingVertical: 4,
},
selectionIndicator: {
height: 20,
width: 20,
borderRadius: 10,
borderWidth: 1,
borderColor: "#000",
alignItems: "center",
justifyContent: "center",
marginRight: 8,
},
selectionInner: {
height: 10,
width: 10,
borderRadius: 5,
backgroundColor: "#000",
},
credentialText: {
fontSize: 16,
},
});
```
This component displays all existing credentials that match the verification request, and provides a
UI for the user to select the credential they wish to share.
Identifiers of the selected credentials are assigned to the `selectedCredentialIds` variable, making
them available for use in the next steps.
2. Add the following code under the `// Step 2.2: Import Credential selector component` comment in
the `proximity-presentation.tsx` file to import the `RequestCredentialSelector` component created
in the previous step:
```ts title="app/proximity-presentation.tsx"
import RequestCredentialSelector from "@/components/RequestCredentialSelector";
```
Before we can display and use the `RequestCredentialSelector` component, we need to implement a
functionality that allows users to select which credentials they want to share.
The `handleToggleSelection` function updates the `selectedCredentialIds` state array when users tap
on credentials. This function is passed to the `RequestCredentialSelector` component to handle
selection state, and the resulting array of selected credential identifiers will be used when
sending the presentation response to the verifier.
3. Add the following code under the `// Step 2.3: Add handleToggleSelection function` comment to
create the `handleToggleSelection` function:
```ts title="app/proximity-presentation.tsx"
const handleToggleSelection = useCallback((id: string) => {
setSelectedCredentialIds(
(prev) =>
prev.includes(id)
? prev.filter((item) => item !== id) // Remove if already selected
: [...prev, id], // Add if not selected
);
}, []);
```
4. Add the following code under the `{/* Step 2.4: Display request details */}` comment to display
the `RequestCredentialSelector` component to the user, enabling them to select which credentials
to share:
```ts title="app/proximity-presentation.tsx"
```
5. Run the app, select the **Present Credential** button and the use your verifier testing
device to scan the displayed QR code. You should see a result similar to the following:
* As the user selects the **Present Credential** button a new proximity presentation session is
created and a QR code is displayed.
* When a verifier application scans the QR code, the devices will automatically exchange public keys
to establish a secure communication channel, enabling the verifier to send a presentation request,
which details:
* What credentials are required.
* What specific claims are required from these credentials.
* The request details are then displayed by the `RequestCredentialSelector` component and enable the
user to select which matching credential to share with the verifier.
We will implement the logic to share the information with the verifier in the next step.
### Step 3: Send a response [#step-3-send-a-response]
The next (and final!) capability you need to build is for the wallet application to send a
presentation response upon receiving consent from the user to share information with the verifier.
Once the user provides this consent by selecting the **Send Response** button, the wallet
application should call the SDK's `sendResponse` method to share the selected credentials with the
verifier as a presentation response.
1. In the `ContentView` file replace the print `statement` under the
`// Proximity Presentation - Step 3.1: Send a credential response` comment with the following
code to call the
[`sendResponse`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/proximitypresentationsession/sendresponse\(credentialids:terminatesession:\))
method when the user selects the **Send Response** button:
```swift title="ContentView"
Task {
do {
let _ = try await proximityPresentationSession?.sendResponse(credentialIds: [id])
// set presentation session to nil after sending a response
proximityPresentationSession = nil
// Return to root view after the response is sent
navigationPath = NavigationPath()
} catch {
print(error)
}
}
```
The
[`sendResponse`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/proximitypresentationsession/sendresponse\(credentialids:terminatesession:\))
function signs the presentation response with the user’s device private key (to prove
[Device authentication](/docs/concepts/mdocs/core-capabilities#device-authentication)) and shares it as an encoded
CBOR file.
1. Open the `PresentationSelectCredentialsScreen.kt` file and add the following code under the
`// Step 3.1: Create function to send the credential response` comment to create a new function
that calls the SDK's
[`sendResponse`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-proximity-presentation-session/send-response.html)
method.
```kotlin title="PresentationSelectCredentialsScreen.kt"
private suspend fun sendResponse(credentialId: String, activity: Activity) {
MobileCredentialHolder.getInstance()
.getCurrentProximityPresentationSession()
?.sendResponse(listOf(credentialId), activity)
}
```
The
[`sendResponse`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-proximity-presentation-session/send-response.html)
function signs the presentation response with the user’s device private key (to prove
[Device authentication](/docs/concepts/mdocs/core-capabilities#device-authentication)) and shares it as an encoded
CBOR file.
2. Add the following code under the `// Step 3.2: Send response` to call the created `sendResponse`
function when the user selects the "Send Response" button:
```kotlin title="PresentationSelectCredentialsScreen.kt"
Button(
onClick = {
coroutineScope.launch { sendResponse(selectedCredentialId, activity) }
},
Modifier.fillMaxWidth()
) { Text("Send Response") }
```
1. Add the following code under the `// Step 3.1: Add handleSendResponse function` comment to
create a new `handleSendResponse` function that calls the SDK's
[`sendProximityPresentationResponse`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/sendProximityPresentationResponse.html)
function:
```ts title="app/proximity-presentation.tsx"
const handleSendResponse = useCallback(async () => {
if (selectedCredentialIds.length === 0) {
Alert.alert(
"No Credentials Selected",
"Please select at least one credential to send.",
);
return;
}
try {
const result = await sendProximityPresentationResponse({
credentialIds: selectedCredentialIds,
});
if (result.isErr()) {
await terminateSession();
throw new Error(
`Error sending proximity response: ${result.error.message || result.error.type}`,
);
}
Alert.alert("Success", "Presentation response sent successfully!");
navigateToIndex();
} catch (err: unknown) {
handleError(err instanceof Error ? err.message : String(err));
}
}, [selectedCredentialIds, handleError, terminateSession, navigateToIndex]);
```
2. Add the following code under the `{/* Step 3.2: Send response */}` comment to add a new button
that calls the `handleSendResponse` function created in the previous step:
```ts title="app/proximity-presentation.tsx"
Share credential
```
The
[`sendProximityPresentationResponse`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/sendProximityPresentationResponse.html)
function signs the presentation response with the user’s device private key (to prove
[Device authentication](/docs/concepts/mdocs/core-capabilities#device-authentication)) and shares it as an encoded
CBOR file.
### Step 4: Test the application [#step-4-test-the-application]
1. Run the app.
2. Select the **Present Credentials** button.
3. Use your testing verifier app to scan the presented QR code and send a presentation request.
4. Back on the holder device, select the matching credential to share and select the **Send
Response** button.
You should see a result similar to the following:
As the user selects the credential to share, the verifier app will receive the presentation
response, verify any incoming credentials and display the verification results.
Congratulations, you have now completed this tutorial, and should have a working wallet application
that can claim an mDoc using an OID4VCI workflow, and present it to a verifier via a proximity
presentation workflow.
## Summary [#summary]
You have just used the [mDocs Holder SDKs](/docs/holding/sdk-overview) to build an application
that can present a claimed [mDoc](/docs/concepts/mdocs) via a proximity presentation workflow as per
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
This was achieved by building the following capabilities into the application:
* Generating a QR code for the verifier to scan and establish a secure communication channel.
* Receive and handle a presentation request from the verifier.
* Display matching credentials to the user and ask for consent to share them with the verifier.
* Send matching credentials to the verifier as a presentation response.
## What's next? [#whats-next]
* You can build additional capabilities into your new application:
* Present a claimed mDoc for verification via an
[online presentation workflow](/docs/holding/remote-presentation-tutorial) into your
new application.
* You can check out the SDKs reference documentation for more details on the available functions and
classes:
* [iOS](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk)
* [Android](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/index.html)
* [React Native](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/index.html)
# Remote presentation
URL: /docs/holding/remote-presentation-overview
Remote presentation allows holders to present a credential for verification without the need for a
physical presence. This is particularly useful for scenarios where the verifier and holder are not
in the same location, such as online transactions or remote identity verification processes.
## mDocs [#mdocs]
The [ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html)
specification establishes interoperable methods for remote presentation and verification of
mDocs such as mobile driver’s licenses (**mDLs**) and other digital credentials.
### Verification requests [#verification-requests]
The [ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) specification defines two key
aspects of remote verification requests:
1. **Wallet interaction**: Defines how the request and response are transferred between the verifier
and the user's wallet:
* HTTP Redirects: Standard HTTP redirects are used to pass information between the verifier and
the wallet, typically via a web browser.
* Digital Credentials API (DC API): A browser-integrated API for digital credential
interactions. The DC API allows web apps to communicate securely and seamlessly with wallet
apps, improving user experience by eliminating multiple browser redirects.
2. **Request type**: Defines how the verifier requests the credential from the user's device and how
should the device respond:
* Device retrieval: Based on the [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html)
standard and defined in ISO/IEC 18013-7 Annex A. The verifier directly requests a credential
from the user's device, and the device retrieves and presents the necessary credential data in
response.
* [OID4VP (OpenID for Verifiable Presentations)](/docs/verification/oid4vp): Based
on the [OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html) protocol
and defined in ISO/IEC 18013-7 Annex B. The verifier sends an authentication request, and the
device responds with a verifiable presentation containing the requested data.
#### ISO/IEC 18013-7:2025 Annexes [#isoiec-18013-72025-annexes]
ISO/IEC 18013-7:2025 defines specific annexes for each combination of wallet interaction and request
type:
| Annex | Request type | Transfer method | Platform support |
| ----- | ---------------- | ----------------------- | ------------------------------------------------------- |
| A | Device Retrieval | HTTPs | General purpose, browser-based interactions |
| B | OID4VP | HTTPs Redirects | General purpose, browser-based interactions |
| C | Device Retrieval | Digital Credentials API | Supported on **iOS** devices and the **Safari** browser |
The next iteration of ISO/IEC 18013-7 is expected to define an additional Annex D:
| Annex | Request type | Transfer method | Platform support |
| ----- | ------------ | ----------------------- | ---------------------------------------------------------------- |
| D | OID4VP | Digital Credentials API | Supported on **Android** devices and **Chromium-based** browsers |
### Presentation flows [#presentation-flows]
The [ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) specification defines different
flows for requesting and presenting mDocs based on the type of verifier application and the
responding device:
* **Verifier web applications:** Typically use HTTP redirects or the Digital Credentials API to
invoke the wallet from a desktop or mobile browser. Web applications support same-device and
cross-device flows:
* **Same-device flow:** You start the verification experience in a mobile browser and are
redirected to a wallet app (where your mDoc is stored) installed on the same mobile device to
respond to the request.
For example, an online banking portal accessed from your mobile browser prompts you to open
your wallet app on the same phone to present your mDoc for identity verification.
* **Cross-device flow:** You start the verification experience in a desktop browser and use your
mobile device (where your mDoc is stored in a wallet app) to respond to the verification
request.
For example, you visit a government tax website on your desktop computer, and scan a QR code
with your mobile wallet app to present your mDoc.
* **Verifier mobile applications:** Can use platform capabilities or app-to-app communication to
initiate and complete the verification. Similar to web applications, mobile applications support
both same-device and cross-device flows:
* **Same-device flow:** You start the experience on a mobile app, and are redirected to a wallet
app installed on the same device to present the credential.
For example, a tax filing mobile app redirects you to your wallet app on the same phone to
verify your identity with your mDoc before filing your return.
* **Cross-device flow:** You start the experience on a **mobile app**, but use a **different
mobile device** to present the credential. This may happen if your credential is only
available on another device you own.
For example, you use your tablet to access a government service app, but the app enables you
to scan a QR code using your phone where the required mDoc is available in a wallet app.
The exact protocols and flows used depend on the requesting application and the user's wallet.
Different wallets and browsers support different combinations of these request types and transfer
methods.
# Learn how to build an application that can present an mDoc remotely
URL: /docs/holding/remote-presentation-tutorial
## Introduction [#introduction]
In this tutorial you will use the [mDocs Holder SDKs](/docs/holding/sdk-overview) to build an
application that can present a claimed [mDoc](/docs/concepts/mdocs) to a verifier remotely via a
[remote presentation workflow](/docs/verification/remote-overview) as per
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html).
This app will support both same-device and cross-device workflows to accommodate flexible user journeys.
### Same-device workflow [#same-device-workflow]
1. The user interacts with a website on their mobile device browser.
2. The user is asked to present information as part of the interaction.
3. The user is redirected to the application you will build in this tutorial.
4. The application authenticates the user.
5. The user is informed of what information they are about to share and provide their consent.
6. The user is redirected back to the browser where verification results are displayed, enabling
them to continue with the interaction.
The result will look something like this:
### Cross-device workflow [#cross-device-workflow]
1. The user interacts with a website on their desktop browser.
2. The user is asked to present information as part of the interaction.
3. The user scans a QR code using a mobile device where the tutorial application is installed.
4. The tutorial application is launched on the mobile device.
5. The tutorial application authenticates the user.
6. The user is informed of what information they are about to share and provide their consent.
7. Verification results are displayed in the user's desktop browser, enabling them to continue with
the interaction.
The result will look something like this:
## Prerequisites [#prerequisites]
Before you get started, let's make sure you have everything you need.
### Prior knowledge [#prior-knowledge]
* The presentation workflow described in this tutorial is based on
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html). If you are
unfamiliar with these technical specifications, refer to the following resources for more
information:
* What are [mDocs](/docs/concepts/mdocs)?
* What is [credential verification](/docs/verification)?
* Breakdown of the [remote presentation workflow](/docs/verification/remote-overview).
* We assume you have experience developing applications in the relevant programming languages and
frameworks (Swift for iOS, Kotlin for Android and TypeScript for React Native).
If you need to get a holding solution up and running quickly with minimal
development resources and in-house domain expertise, [talk to
us](http://mattr.global/contact-us) about our white-label [MATTR GO Hold
app](https://mattr.global/platforms/go) which might be a good fit for you.
### Prerequisite tutorial [#prerequisite-tutorial]
* You must complete the
[Claim a credential tutorial](/docs/holding/credential-claiming-tutorial) and claim the mDoc
provided in the tutorial.
* This application is used as the base for the current tutorial.
### Testing devices [#testing-devices]
* Supported iOS device to run the built application on, setup with:
* Biometric authentication (Face ID, Touch ID).
* Available internet connection.
* Supported Android device to run the built application on, setup with:
* Biometric authentication (Face recognition, fingerprint recognition).
* Available internet connection.
* [Debugging](https://developer.android.com/studio/debug/dev-options#enable) enabled.
* Supported iOS and/or Android device to run the built application on, setup with:
* Available internet connection.
* iOS:
* Biometric authentication (Face ID, Touch ID).
* Android:
* Biometric authentication (Face recognition, fingerprint recognition).
* [Debugging](https://developer.android.com/studio/debug/dev-options#enable) enabled.
Got everything? Let's get going!
## Tutorial steps [#tutorial-steps]
To enable a user to present a stored [mDoc](/docs/concepts/mdocs) to a verifier via an
[online presentation workflow](/docs/verification/remote-overview), you will build the following
capabilities into your application:
1. Register the verifier's Authorization endpoint.
2. Create an online presentation session.
3. Handle a presentation request.
4. Send a presentation response.
### Step 1: Register the verifier's Authorization endpoint [#step-1-register-the-verifiers-authorization-endpoint]
The [Authorization endpoint](/docs/verification/remote-overview#authorization-request) is a URI
associated with an application identifier in the MATTR VII tenant configuration. It is used to
[invoke an application](/docs/verification/remote-web-verifiers/workflow#the-mattr-vii-verifier-tenant-responds-with-a-link-to-invoke-a-matching-application)
that will handle the presentation request. The application then uses the URI to retrieve a request
object, which details what information is required for verification.
Online verifiers are recommended to generate this URI as a
[Universal link](https://developer.apple.com/ios/universal-links/) for iOS and
[App Link](https://developer.android.com/training/app-links#android-app-links) for Android, as this
enables them to explicitly define and validate applications that can respond to their verification
requests.
However, for simplicity reasons in this tutorial our verifier is using a
[custom URI scheme](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app)
for iOS and a [deep link](https://developer.android.com/training/app-links#deep-links) for Android,
both matching the default scheme defined by the OID4VP specification (`mdoc-openid4vp`). This means
that you need to configure the application to be able to handle this custom URI scheme.
1. Open the Xcode project with the application built in the
[Claim a credential](/docs/holding/credential-claiming-tutorial) tutorial.
2. Register `mdoc-openid4vp` as a recognized
[URL scheme](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app):
* Open the project view and select your application target.
* Select the *Info* tab.
* Scroll down and expand the *URL Types* area.
* Select the plus button.
* Insert `mdoc-openid4vp` in both the *Identifier* and *URL Schemes* fields.
3. [Run](https://developer.apple.com/documentation/xcode/running-your-app-in-simulator-or-on-a-device)
the app and then close it (this updates the app on your testing device) and perform the following
instructions:
* Use a desktop browser to navigate to the [MATTR Labs remote presentation testing tool](https://tools.mattrlabs.com/verify-credentials).
* Select **OID4VP (Redirect)** from the *Select Experience* list.
* Select **Request credentials**.
* Open the camera on your testing mobile device and scan the QR code.
* Confirm opening the QR code with your tutorial application.
* The tutorial application should be launched on your testing mobile device.
1. Open the Android Studio project with the application built in the
[Claim a credential](/docs/holding/credential-claiming-tutorial) tutorial.
2. Open your `AndroidManifest.xml`.
3. Add the following
[intent filter](https://developer.android.com/guide/components/intents-filters#Receiving) to your
`MainActivity`:
```xml title="AndroidManifest.xml"
```
4. [Run](https://developer.android.com/studio/run#basic-build-run) the app and then close it (this
updates the app on your testing device) and perform the following instructions:
* Use a desktop browser to navigate to the [MATTR Labs remote presentation testing tool](https://tools.mattrlabs.com/verify-credentials).
* Select **OID4VP (Redirect)** from the *Select Experience* list.
* Select **Request credentials**.
* Open the camera on your testing mobile device and scan the QR code.
* Confirm opening the QR code with your tutorial application.
* The tutorial application should be launched on your testing mobile device.
1. Open the project with the application built in the
[Claim a credential](/docs/holding/credential-claiming-tutorial) tutorial.
2. Open the `app.config.ts` file and replace the `scheme` property assignment under the
`// Online presentation - Step 1.1: Update application custom scheme` comment with the following:
```ts title="app.config.ts"
scheme: "mdoc-openid4vp",
```
3. Delete the `ios` and `android` directories in the project's root. This is required to ensure the
scheme changes are applied correctly.
### Step 2: Create an online presentation session [#step-2-create-an-online-presentation-session]
Now that the application can handle an OID4VP custom URI scheme, the next step is to build the
capability to use the request URI to retrieve the request object. This object details:
* What credentials are required.
* What specific claims are required from these credentials.
* What MATTR VII tenant to interact with.
1. In your project's `ContentView` file, add the following code under the
`// Online Presentation - Step 2.1: Create a variable to hold the online presentation session object`
comment to create a variable that will hold the online presentation session:
```swift title="ContentView"
var onlinePresentationSession: OnlinePresentationSession?
```
The following step is also included in the [Proximity presentation
tutorial](/docs/holding/proximity-presentation-tutorial). If you had already
completed this tutorial you may skip to step 3.
2. Add the following code under the
`// Proximity and Online Presentation: Create variables for credential presentations` comment to
create the following variables:
```swift title="ContentView"
var matchedCredentials: [MobileCredential] = []
var matchedMetadata: [MobileCredentialMetadata] = []
var credentialRequest: [MobileCredentialRequest] = []
```
* `matchedCredentials` : Holds stored credentials that match the credential request.
* `matchedMetadata` : Holds metadata of credentials that match the credential request.
* `credentialRequest`: Holds the credentials that were requested for verification.
3. Replace the `print` statement under the
`// Online Presentation - Step 2.3: Create online presentation session` comment with the
following code to create a function that calls the SDK's
[`createOnlinePresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/createonlinepresentationsession\(authorisationrequesturi:requiretrustedverifier:\))
method with the `authorizationRequestURI` parameter (the request URI retrieved from the link/QR
code):
```swift title="ContentView"
Task {
do {
onlinePresentationSession = try await mobileCredentialHolder.createOnlinePresentationSession(authorizationRequestUri: authorizationRequestURI, requireTrustedVerifier: false)
let matched = onlinePresentationSession?.getMatchedCredentials() ?? []
matchedMetadata = matched
.flatMap { $0.matchedMobileCredentials }
credentialRequest = matched
.map { $0.request }
} catch {
print(error.localizedDescription)
}
}
```
This function:
* Creates an `OnlinePresentationSession` instance and assigns it to the
`onlinePresentationSession` variable.
* Stores matched
[`MobileCredentialMetadata`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialmetadata)
in the `matchedMetadata` variable and the
[`MobileCredentialRequest`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialrequest)
in the `credentialRequest` variable in our `ViewModel` to enable displaying these values to
the user.
We chose to set `requireTrustedVerifier` parameter to `false` because we want
the SDK to trust all verifiers by default. If you require to limit the
verifiers a user can interact with, you may want to manually add trusted
verifier certificates and set the parameter to `true`. You can learn more
about certificate management in our [SDK
docs.](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/addtrustedverifiercertificates\(certificates:\))
4. Add the following code under the
`// Online Presentation - Step 2.4: Create session from request URI` comment to add an
[onOpenURL modifier](https://developer.apple.com/documentation/swiftui/view/onopenurl\(perform:\))
that will call the `createOnlinePresentationSession` function when the application is launched
following selecting a link (same-device flow) or scanning a QR code (cross-device flow) that
includes a registered URI:
```swift title="ContentView"
.onOpenURL { url in
Task {
await viewModel.createOnlinePresentationSession(authorizationRequestURI: url.absoluteString)
}
// Navigate to online presentation view
viewModel.navigationPath.append(NavigationState.onlinePresentation)
}
```
Now, once a user opens an online presentation link on their device, an online presentation
session will be created the user will be navigated to a new view, which you will implement in
the next step.
1. Create a new file named `OnlinePresentationScreen.kt` and add the following code to the file:
```kotlin title="OnlinePresentationScreen.kt"
package com.example.holdertutorial
import android.app.Activity
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import global.mattr.mobilecredential.holder.deviceretrieval.devicerequest.DataElements
import global.mattr.mobilecredential.holder.deviceretrieval.deviceresponse.NameSpace
import global.mattr.mobilecredential.holder.dto.MobileCredential
import global.mattr.mobilecredential.holder.dto.MobileCredentialMetaData
import global.mattr.mobilecredential.holder.MobileCredentialHolder
import global.mattr.mobilecredential.holder.onlinepresentation.OnlinePresentationSession
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@Composable
fun OnlinePresentationScreen(activity: Activity, requestUri: String) {
var session: OnlinePresentationSession? by remember { mutableStateOf(null) }
// Step 2.1: Create an online presentation session
val (requested, matched) = session?.getMatchedCredentials()?.entries?.firstOrNull() ?: return
var matchedCredentials by remember { mutableStateOf(matched) }
var selectedCredentialId by remember { mutableStateOf(matchedCredentials.first().id) }
val coroutineScope = rememberCoroutineScope()
Column(Modifier.verticalScroll(rememberScrollState())) {
Text("REQUESTED DATA", style = MaterialTheme.typography.titleLarge)
Card(Modifier.padding(vertical = 8.dp)) {
Document(requested.docType, requested.namespaces.value.toUi())
}
Spacer(Modifier.padding(12.dp))
Text("MATCHED CREDENTIALS", style = MaterialTheme.typography.titleLarge)
Spacer(Modifier.padding(6.dp))
matchedCredentials.forEach { matchedCredential ->
// Step 3.2: Display matching credentials and claims
}
// Step 4.1: Send response
}
}
// Step 3.1: Create function to add values to claims
private fun Map.toUi() = mapValues { (_, dataElements) ->
dataElements.value.keys.toSet()
}
```
This code is very similar to the one used in the in the
`PresentationSelectCredentials.kt` file in the [Proximity
Presentation](/docs/holding/proximity-presentation-tutorial) tutorial, to
avoid creating dependencies between the tutorials. In your own project you can
use the same components for both presentation workflows.
2. Add the following code under the `// Step 2.1: Create an online presentation session` comment to
create a new online presentation session when the `OnlinePresentationScreen` composable appears
on the screen:
```kotlin title="OnlinePresentationScreen.kt"
LaunchedEffect(requestUri) {
withContext(Dispatchers.IO) {
val mdocHolder = MobileCredentialHolder.getInstance()
while (!mdocHolder.initialized) delay(100)
session = mdocHolder
.createOnlinePresentationSession(requestUri, requireTrustedVerifier = false)
}
}
```
This calls the SDK's
[createOnlinePresentationSession](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/create-online-presentation-session.html)
function, passing `requestUri` as an argument, which is the Authorization request URI retrieved from
the deep link/QR code. The function returns an
[OnlinePresentationSession](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.onlinepresentation/-online-presentation-session/index.html)
object which is stored in the declared `session` variable.
This object provides a `getMatchedCredentials()` method, which details the requested information
([MobileCredentialRequest](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.dto/-mobile-credential-request/index.html))
and any existing credentials that match it
([MobileCredentialMetaData](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.dto/-mobile-credential-meta-data/index.html)).
We will use this information in the next steps to display this information to the user.
We must wait for the SDK instance to be initialized, because in this tutorial the SDK initialization
is called in a coroutine in the `MainActivity.onCreate` method.
We chose to set `requireTrustedVerifier` parameter to `false` because we want
the SDK to trust all verifiers by default. If you require to interact with a
limited list of verifiers, you may want to manually add trusted verifier
certificates and set the parameter to `true`. You can learn more about
certificate management in our [SDK
docs](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/add-trusted-verifier-certificates.html).
3. In your `MainActivity.kt` file, add the following code under the
`// Online Presentation - Step 2.2: Add "Online Presentation" screen call` comment to connect the
created `OnlinePresentationScreen` composable to the navigation graph:
```kotlin title="MainActivity.kt"
composable(
"onlinePresentation",
deepLinks = listOf(
navDeepLink { uriPattern = "mdoc-openid4vp://{wildcard}" }
)
) {
@Suppress("DEPRECATION")
val deepLink = it.arguments
?.getParcelable(NavController.KEY_DEEP_LINK_INTENT)
?.data
?.toString() ?: ""
OnlinePresentationScreen(this@MainActivity, deepLink)
}
```
Previously you
[added an intent filter](/docs/holding/remote-presentation-tutorial#register-the-verifiers-authorization-endpoint)
for [deep links](https://developer.android.com/training/app-links#deep-links) with a
`mdoc-openid4vp` scheme, so that the app is started when the intent with the deep link is filtered.
Now, when the app is opened via this deep link, it will also start the `OnlinePresentationScreen`
composable, and pass the deep link as the `requestUri` argument.
1. In your project's `app` directory, create a new file named `online-presentation.tsx` and add the
following scaffolding code:
```ts title="app/online-presentation.tsx"
// Step 3.2: Import Credential selector component
import { useHolder } from "@/providers/HolderProvider";
import {
type OnlinePresentationSession,
createOnlinePresentationSession,
} from "@mattrglobal/mobile-credential-holder-react-native";
import { useGlobalSearchParams, useRouter } from "expo-router";
import React, { useState, useEffect, useCallback } from "react";
import {
Alert,
StyleSheet,
Text,
TouchableOpacity,
View,
} from "react-native";
export default function OnlinePresentation() {
const router = useRouter();
const { scannedValue: authorizationRequestUri } = useGlobalSearchParams<{
scannedValue: string;
}>();
const { isHolderInitialized } = useHolder();
const [onlinePresentationSession, setOnlinePresentationSession] =
useState(null);
const [error, setError] = useState(null);
const [requests, setRequests] = useState<
OnlinePresentationSession["matchedCredentials"]
>([]);
const [selectedCredentialIds, setSelectedCredentialIds] = useState<
string[]
>([]);
const handleError = useCallback((message: string) => {
setError(message);
console.error(message);
}, []);
// Step 3.3: Add handleToggleSelection function
// Step 2.6: Create Online Presentation Session
// Step 4.1: Add handleSendResponse function
if (error) {
return (
Error: {error}
);
}
if (!onlinePresentationSession) {
return (
No online presentation session
);
}
// Step 3.4: Display presentation session details
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
},
errorText: {
color: "red",
fontSize: 16,
},
button: {
backgroundColor: "#007AFF",
paddingVertical: 12,
paddingHorizontal: 20,
borderRadius: 8,
alignItems: "center",
marginTop: 20,
},
buttonText: {
color: "white",
fontSize: 16,
fontWeight: "600",
},
verifierSection: {
backgroundColor: "#f0f0f0",
padding: 10,
borderRadius: 8,
marginBottom: 15,
},
label: {
fontWeight: "bold",
fontSize: 14,
textTransform: "uppercase",
marginBottom: 5,
},
verifierText: {
fontSize: 16,
},
});
```
2. Open the `app/index.tsx` file and add the following code inside the `handleScanComplete` function
under the `// Online Presentation - Step 2.2: Handle the 'mdoc-openid4vp://' scheme prefix`
comment:
```ts title="app/index.tsx"
else if (scannedValue.startsWith('mdoc-openid4vp://')) {
router.replace({
pathname: '/online-presentation',
params: { scannedValue }
})
}
```
The application will now redirect to the `online-presentation` screen whenever a QR code which
includes a link prefixed with `mdoc-openid4vp://` is scanned. This handles invoking the correct
screen in cross-device workflows.
Now, we need to make sure the application navigates to the `online-presentation` screen when
following deep links that are prefixed with `mdoc-openid4vp://` as part of same-device workflows.
Add the following code to the `HolderProvider` component to handle linking:
3. Open the `app/providers/HolderProvider.tsx` and add the following code under the
`// Online Presentation - Step 2.3: Import expo-linking and expo-router` to import the required
redirect components:
```ts title="app/providers/HolderProvider.tsx"
import * as Linking from "expo-linking";
import { useRouter } from "expo-router";
```
4. Add the following code under the `// Online Presentation - Step 2.4: Initialize router variable`
comment to use the imported `useRouter` component:
```ts title="app/providers/HolderProvider.tsx"
const router = useRouter();
```
5. Add the following code under the `// Online Presentation - Step 2.5: Handle deep link` comment to
use the `router` component redirect the user to the `online-presentation` screen when following a
deep link prefixed with `mdoc-openid4vp://`:
```ts title="app/providers/HolderProvider.tsx"
useEffect(() => {
if (!isHolderInitialized) return;
const handleDeepLink = (event: { url: string }) => {
const { url } = event;
console.log("Deep link received:", url);
if (url.startsWith("mdoc-openid4vp://")) {
router.replace({
pathname: "/online-presentation",
params: { scannedValue: url },
});
}
};
Linking.getInitialURL().then((url) => {
if (url) {
console.log("Initial URL:", url);
handleDeepLink({ url });
}
});
const subscription = Linking.addEventListener("url", handleDeepLink);
return () => subscription.remove();
}, [isHolderInitialized, router]);
```
This function was created in the [Claim a credential
tutorial](/docs/holding/credential-claiming-tutorial) to handle QR codes which
include OID4VCI credential offers.
6. Return to the `online-presentations.tsx` file and add the following code under the
`// Step 2.6: Create Online Presentation Session` comment to create a function that calls the
SDK's
[`createOnlinePresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/createOnlinePresentationSession.html)
function with the `authorizationRequestURI` parameter (the request URI retrieved from the deep
link/QR code) to create an `OnlinePresentationSession` instance and assign it to the `session`
variable:
```ts title="app/online-presentation.tsx"
useEffect(() => {
if (!isHolderInitialized || !authorizationRequestUri) return;
const createSession = async () => {
try {
const result = await createOnlinePresentationSession({
authorizationRequestUri,
requireTrustedVerifier: false,
});
if (result.isErr()) {
throw new Error(
result.error.message || "Error creating presentation session",
);
}
setOnlinePresentationSession(result.value);
if (result.value.matchedCredentials) {
setRequests(result.value.matchedCredentials);
}
} catch (err: unknown) {
handleError(err instanceof Error ? err.message : String(err));
}
};
createSession();
}, [isHolderInitialized, authorizationRequestUri, handleError]);
```
Once the `result` (the response returned by the
[`createOnlinePresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/createOnlinePresentationSession.html)
function) is available, your application can access its `matchedCredentials` object, which contains
the list of credentials that match the verifier's request. Your application will use this
information to display the request details and allow the user to select which credentials to share.
We chose to set `requireTrustedVerifier` parameter to `false` because we want
the SDK to trust all verifiers by default. If you require to interact with a
limited list of verifiers, you may want to manually add trusted verifier
certificates and set this parameter to `true`. You can learn more about
certificate management in our [SDK
docs.](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/addTrustedVerifierCertificates.html)
7. Open the `app/index.tsx` file and add the following code under the
`{/* Online Presentation - Step 2.7: Add Online Presentation button */}` comment to add a button
that will open the scanner and enable the user to scan a QR code and start an online presentation
session:
```ts title="app/index.tsx"
setIsScannerVisible(true)}
>
Online Presentation
```
8. Run the app for your targeted device (using `yarn android --device` and/or `yarn ios --device` to
rebuild the deleted folders) and perform the following instructions:
* Use a desktop browser to navigate to the [MATTR Labs remote presentation testing tool](https://tools.mattrlabs.com/verify-credentials).
* Select **OID4VP (Redirect)** from the *Select Experience* list.
* Select **Request credentials**.
* Open the camera on your testing mobile device and scan the QR code.
* Confirm opening the QR code with your tutorial application.
* The tutorial application should be launched on your testing mobile device.
You have not yet implemented the logic to handle the presentation request, so the application will
not display any information at this stage. Let's proceed to the next step to fix that.
### Step 3: Handle a presentation request [#step-3-handle-a-presentation-request]
We will now build the capability to use information retrieved by the
`createOnlinePresentationSession` function to handle the presentation request. This includes:
* Displaying what information is requested.
* Displaying what existing credentials match the requested information.
* Displaying what information from these existing claims will be shared with the verifier.
* Asking for the user's consent to share requested information from matching credentials.
The following two steps are also included in the [Proximity presentation
tutorial](/docs/holding/proximity-presentation-tutorial). If you had already
completed this tutorial you may skip to step 3.
1. Replace the `print` statement under the
`// Proximity and Online Presentation: Retrieve a credential from storage` comment with the
following code create a function that uses the SDK's
[getCredential](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/getcredential\(credentialid:fetchupdatedstatuslist:\))
method to retrieve a credential from the application storage:
```swift title="ContentView"
Task {
do {
let credential = try await mobileCredentialHolder.getCredential(credentialId: id)
matchedCredentials.append(credential)
} catch {
print(error)
}
}
```
The
[MobileCredentialMetadata](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialmetadata)
object does not include the values of claims included in the credential. To display these
values, the above function calls the SDK's
[getCredential](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/getcredential\(credentialid:fetchupdatedstatuslist:\))
method with the `id` property of the
[MobileCredentialMetadata](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialmetadata).
2. Create a new file called `PresentCredentialsView.swift` and paste the following code to create a
view to display credential requests and matching credentials stored in the application:
```swift title="PresentCredentialsView"
import MobileCredentialHolderSDK
import SwiftUI
struct PresentCredentialsView: View {
var viewModel: PresentCredentialsViewModel
@State var selectedID: String?
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 20) {
Text("Requested Documents")
.font(.headline)
.padding(.leading)
ForEach(viewModel.requestedDocuments, id: \.docType) { requestedDocument in
DocumentView(viewModel: DocumentViewModel(from: requestedDocument))
}
Text("Matched Credentials")
.font(.headline)
.padding(.leading)
ForEach(viewModel.matchedMetadata, id: \.id) { matchedMetadata in
VStack(alignment: .leading, spacing: 10) {
if let matchedCredential = viewModel.matchedMobileCredential(id: matchedMetadata.id) {
DocumentView(viewModel: DocumentViewModel(from: matchedCredential))
.padding(.vertical)
.background(selectedID == matchedMetadata.id ? Color.blue.opacity(0.2) : Color.clear)
.onTapGesture {
guard selectedID != matchedMetadata.id else {
selectedID = nil
return
}
selectedID = matchedMetadata.id
}
Button("Hide claim values") {
viewModel.matchedCredentials.removeAll(where: { $0.id == matchedMetadata.id })
}
.frame(maxWidth: .infinity, alignment: .center)
} else {
DocumentView(viewModel: DocumentViewModel(from: matchedMetadata))
.padding(.vertical)
.background(selectedID == matchedMetadata.id ? Color.blue.opacity(0.2) : Color.clear)
.onTapGesture {
guard selectedID != matchedMetadata.id else {
selectedID = nil
return
}
selectedID = matchedMetadata.id
}
Button("Show claim values") {
viewModel.getCredentialAction(matchedMetadata.id)
}
.frame(maxWidth: .infinity, alignment: .center)
}
}
}
}
}
if selectedID != nil {
Button("Send Response") {
viewModel.sendCredentialAction(selectedID!)
}
.buttonStyle(.borderedProminent)
.clipShape(Capsule())
.frame(maxWidth: .infinity, alignment: .center)
}
}
}
// MARK: PresentCredentialsViewModel
class PresentCredentialsViewModel {
@Binding var requestedDocuments: [MobileCredentialRequest]
@Binding var matchedCredentials: [MobileCredential]
@Binding var matchedMetadata: [MobileCredentialMetadata]
var getCredentialAction: (String) -> Void
var sendCredentialAction: (String) -> Void
init(
requestedDocuments: Binding<[MobileCredentialRequest]>,
matchedCredentials: Binding<[MobileCredential]>,
matchedMetadata: Binding<[MobileCredentialMetadata]>,
sendCredentialAction: @escaping (String) -> Void,
getCredentialAction: @escaping (String) -> Void
) {
self._requestedDocuments = requestedDocuments
self._matchedCredentials = matchedCredentials
self._matchedMetadata = matchedMetadata
self.sendCredentialAction = sendCredentialAction
self.getCredentialAction = getCredentialAction
}
func matchedMobileCredential(id: String) -> MobileCredential? {
matchedCredentials.first(where: { $0.id == id })
}
}
```
The `PresentCredentialsView` view is used to:
* Display requested information.
* Display stored credentials that include the requested information.
* Enable the user to provide consent to sharing the requested information with the verifier.
The `PresentCredentialsViewModel` object is used to reference values from a credential request.
It takes two closures in its initializer:
* `getCredentialAction: (String) -> Void` is used to display claim values.
* `sendCredentialAction: (String) -> Void` is used to send a credential response to the verifier
once the user selected a credential and provided consent by selecting the **Send Response**
button.
3. Back in your `ContentView` file, Replace `EmptyView` under the
`// Online Presentation - Step 3.3: Display online presentation view` comment with the new view
that you created:
```swift title="ContentView"
PresentCredentialsView(
viewModel: PresentCredentialsViewModel(
requestedDocuments: $viewModel.credentialRequest,
matchedCredentials: $viewModel.matchedCredentials,
matchedMetadata: $viewModel.matchedMetadata,
sendCredentialAction: viewModel.sendOnlinePresentationSessionResponse(id:),
getCredentialAction: viewModel.getCredential(id:)
)
)
```
4. Replace the `return false` statement under the
`// Online Presentation - Step 3.4: View Online Presentation` comment with the following code to
enable the user to manually navigate to the presentation session view if required:
```swift title="ContentView"
onlinePresentationSession != nil
```
5. [Run](https://developer.apple.com/documentation/xcode/running-your-app-in-simulator-or-on-a-device)
the app and then close it (this updates the app on your testing device) and perform the following
instructions:
* Use a desktop browser to navigate to the [MATTR Labs remote presentation testing tool](https://tools.mattrlabs.com/verify-credentials).
* Select **OID4VP (Redirect)** from the *Select Experience* list.
* Select **Request credentials**.
* Open the camera on your testing mobile device and scan the QR code.
* Confirm opening the QR code with your tutorial application.
* The tutorial application should be launched on your testing mobile device, displaying the
verification request and any matching credentials.
* When a credential is selected, it will be highlighted and a **Send Response** button will
appear (the logic associated with the button will be implemented in the next step).
The result will look something like this:
1. In the `OnlinePresentationScreen.kt` file, add the following code under the
`// Step 3.1: Create function to add values to claims` comment to create a new function that
will display the values of the claims the user is about to share:
```kotlin title="OnlinePresentationScreen.kt"
private fun List.withClaimValues(
from: MobileCredential
): List = map { credential ->
if (credential.id != from.id) {
credential
} else {
credential.copy(
claims = credential.claims.mapValues { (namespace, claims) ->
claims.map { claim ->
val claimValue = from.claims[namespace]?.get(claim)
claimValue?.let { "$claim: ${it.toUiString()}" } ?: claim
}.toSet()
}
)
}
}
```
This function retrieves all matching credentials from the
[MobileCredentialMetaData](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.dto/-mobile-credential-meta-data/index.html)
object and retrieves their matching values from the internal storage according to the
credential's `id`.
2. Add the following code under the `// Step 3.2: Display matching credentials and claims` comment
to display to the user what credentials and claims they are about to share with the verifier, as
well as a button that enables the user to display the value of these claims:
```kotlin title="OnlinePresentationScreen.kt"
val borderWidth = if (matchedCredential.id == selectedCredentialId) 4.dp else 0.dp
Column(
Modifier
.clickable { selectedCredentialId = matchedCredential.id }
.border(borderWidth, Color.Blue, RoundedCornerShape(16.dp))
.padding(8.dp)
) {
Card(Modifier.fillMaxWidth()) {
Document(matchedCredential.docType, matchedCredential.claims)
}
Button(
onClick = {
val credentialWithValues = MobileCredentialHolder.getInstance()
.getCredential(matchedCredential.id, fetchUpdatedStatusList = false)
matchedCredentials =
matchedCredentials.withClaimValues(from = credentialWithValues)
},
Modifier.fillMaxWidth()
) { Text("Show Values") }
}
Spacer(Modifier.padding(12.dp))
```
3. [Run](https://developer.android.com/studio/run#basic-build-run) the app and then close it (this
updates the app on your testing device) and perform the following instructions:
* Use a desktop browser to navigate to the [MATTR Labs remote presentation testing tool](https://tools.mattrlabs.com/verify-credentials).
* Select **OID4VP (Redirect)** from the *Select Experience* list.
* Select **Request credentials**.
* Open the camera on your testing mobile device and scan the QR code.
* Confirm opening the QR code with your tutorial application.
* The tutorial application should be launched on your testing mobile device, displaying the
verification request and any matching credentials.
The result will look something like this:
First you will create a component that will display the requested information and allow the user to
select which credentials to share.
The following step is also included in the [Proximity presentation
tutorial](/docs/holding/proximity-presentation-tutorial#handle-a-presentation-request).
If you had already completed this tutorial and created the
`RequestCredentialSelector.tsx` component you may skip to step 3.2.
1. In the `app/components` directory, create a new file named `RequestCredentialSelector.tsx` and
add the following code:
```ts title="app/components/RequestCredentialSelector.tsx"
import type {
MobileCredentialMetadata,
PresentationSessionSuccessRequest,
} from "@mattrglobal/mobile-credential-holder-react-native";
import type React from "react";
import {
FlatList,
type ListRenderItem,
StyleSheet,
Text,
TouchableOpacity,
View,
} from "react-native";
type RequestCredentialSelectorProps = {
requests: PresentationSessionSuccessRequest["request"];
selectedCredentialIds: string[];
onToggleSelection: (credentialId: string) => void;
};
type RequestItem = PresentationSessionSuccessRequest["request"][number];
/**
* Component that renders a list of credential requests and their matched credentials.
*
* @param props - The component props.
* @param props.requests - The list of credential requests.
* @param props.selectedCredentialIds - The list of selected credential IDs.
* @param props.onToggleSelection - Callback function to toggle the selection of a credential.
* @returns The rendered component.
*/
export default function RequestCredentialSelector({
requests,
selectedCredentialIds,
onToggleSelection,
}: RequestCredentialSelectorProps) {
const renderCredential: ListRenderItem = ({
item: cred,
}) => {
const isSelected = selectedCredentialIds.includes(cred.id);
return (
onToggleSelection(cred.id)}
>
{isSelected && }
{cred.branding?.name ?? "Credential"} ({cred.id})
);
};
const renderRequest: ListRenderItem = ({ item }) => (
Request Details
{typeof item.request === "object"
? JSON.stringify(item.request, null, 2)
: item.request}
Matched Credentials: cred.id}
renderItem={renderCredential}
style={styles.credentialsList}
contentContainerStyle={styles.credentialsListContent}
/>
);
return (
idx.toString()}
renderItem={renderRequest}
style={styles.requestsList}
contentContainerStyle={styles.requestsListContent}
/>
);
}
const styles = StyleSheet.create({
requestsList: {
flex: 1,
},
requestsListContent: {
paddingBottom: 10,
},
requestContainer: {
backgroundColor: "#f0f0f0",
padding: 10,
borderRadius: 8,
marginBottom: 15,
},
requestInfo: {
fontStyle: "italic",
fontSize: 12,
marginBottom: 10,
},
label: {
fontWeight: "bold",
fontSize: 14,
textTransform: "uppercase",
marginBottom: 5,
},
credentialsList: {
maxHeight: 200,
},
credentialsListContent: {
paddingBottom: 10,
},
credentialItem: {
flexDirection: "row",
alignItems: "center",
marginBottom: 8,
paddingVertical: 4,
},
selectionIndicator: {
height: 20,
width: 20,
borderRadius: 10,
borderWidth: 1,
borderColor: "#000",
alignItems: "center",
justifyContent: "center",
marginRight: 8,
},
selectionInner: {
height: 10,
width: 10,
borderRadius: 5,
backgroundColor: "#000",
},
credentialText: {
fontSize: 16,
},
});
```
This component displays all existing credentials that match the verification request, and
provides a UI for the user to select the credential they wish to share.
Identifiers of the selected credentials are assigned to the `selectedCredentialIds` variable,
making them available for use in the next steps.
2. Open the `online-presentation.tsx` file and add the following code under the
`// Step 3.2: Import Credential selector component` comment to import the
`RequestCredentialSelector` component created in the previous step:
```ts title="app/online-presentation.tsx"
import RequestCredentialSelector from "@/components/RequestCredentialSelector";
```
Before we can display and use the `RequestCredentialSelector` component, we need to implement a
functionality that allows users to select which credentials they want to share.
The `handleToggleSelection` function updates the `selectedCredentialIds` state array when users tap
on credentials. This function is passed to the `RequestCredentialSelector` component to handle
selection state, and the resulting array of selected credential identifiers will be used when
sending the presentation response to the verifier.
3. Add the following code under the `// Step 3.3: Add handleToggleSelection function` comment to
create the `handleToggleSelection` function:
```ts title="app/online-presentation.tsx"
const handleToggleSelection = useCallback((id: string) => {
setSelectedCredentialIds(
(prev) =>
prev.includes(id)
? prev.filter((item) => item !== id) // Remove if already selected
: [...prev, id], // Add if not selected
);
}, []);
```
4. Add the following code under the `// Step 3.4: Display presentation session details` comment to
display the request details and the `RequestCredentialSelector` component, passing in the
required props:
```ts title="app/online-presentation.tsx"
return (
{/* Display verifier information */}
Verifier:
{onlinePresentationSession.verifiedBy.value}
{/* Component to select credentials for the presentation */}
{/* Step 4.2: Add Send response button */}
);
```
5. Run the app and perform the following instructions:
* Use a desktop browser to navigate to the [MATTR Labs remote presentation testing tool](https://tools.mattrlabs.com/verify-credentials).
* Select **OID4VP (Redirect)** from the *Select Experience* list.
* Select **Request credentials**.
* Open the camera on your testing mobile device and scan the QR code.
* Confirm opening the QR code with your tutorial application.
* The tutorial application should be launched on your testing mobile device.
* The tutorial application should display the verification request and any matching credentials,
enabling the user to select any specific credential for sharing.
The result will look something like this:
### Step 4: Send response [#step-4-send-response]
After displaying matching credentials to the user and enabling them to select what credential to
share, the last thing you need to do is build the capability to share the selected credential with
the verifier.
1. Replace the `print` statement under the
`// Online Presentation - Step 4.1: Send online presentation response` comment with the following
code to call the `sendResponse` method when the user selects the **Send Response** button:
```swift title="ContentView"
Task {
do {
_ = try await onlinePresentationSession?.sendResponse(credentialIds: [id])
// set presentation session to nil after sending a response
onlinePresentationSession = nil
// Return to root view after the response is sent
navigationPath = NavigationPath()
} catch {
print(error)
}
}
```
1. Add the following code under the `// Step 4.1: Send response` comment to add a `Send Response`
button, that will call the SDK's
[sendResponse](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.onlinepresentation/-online-presentation-session/send-response.html)
function and send the selected credential to the Verifier, when pressed:
```kotlin title="OnlinePresentationScreen.kt"
Button(
onClick = {
coroutineScope.launch {
session?.sendResponse(listOf(selectedCredentialId), activity)
}
},
Modifier.fillMaxWidth()
) { Text("Send Response") }
```
1. Add the following code under the `// Step 4.1: Add handleSendResponse function` comment to create
a function that calls the `sendResponse` method of the SDK's `onlinePresentationSession` object
and sends the selected credential to the verifier:
```ts title="app/online-presentation.tsx"
const handleSendResponse = useCallback(async () => {
if (!onlinePresentationSession) return;
if (selectedCredentialIds.length === 0) {
Alert.alert(
"No Credential Selected",
"Please select at least one credential first.",
);
return;
}
try {
const sendResponseResult = await onlinePresentationSession.sendResponse({
credentialIds: selectedCredentialIds,
});
if (sendResponseResult.isErr()) {
throw new Error(
sendResponseResult.error.message ||
"Failed to send presentation response",
);
}
router.replace("/");
Alert.alert("Success", "Presentation response sent successfully!");
} catch (err: unknown) {
const message = err instanceof Error ? err.message : String(err);
handleError(message);
Alert.alert(
"Error",
"Failed to send presentation response. Terminating session...",
);
await onlinePresentationSession.terminateSession();
}
}, [onlinePresentationSession, selectedCredentialIds, router, handleError]);
```
2. Add the following code under the `{/* Step 4.2: Add Send response button */}` comment to create a
button that will enable the user to send a response to the verifier after selecting which
credentials to share:
```ts title="app/online-presentation.tsx"
Send Response
```
### Step 5: Test the application [#step-5-test-the-application]
Let's test that the application is working as expected in both workflows.
#### Same-device workflow [#same-device-workflow-1]
1. Run the app and then close it (this updates the app on your testing device).
2. Use a browser on your testing mobile device to navigate to the
[MATTR Labs remote presentation testing tool](https://tools.mattrlabs.com/mdoc-online-presentation).
3. Select the **OID4VP (Redirect)** option from the *Select Experience* list.
4. Select **Request credentials**.
5. Select **Allow** to open the tutorial application.
6. The tutorial application should be launched on your testing mobile device.
7. Select the credential you wish to send to the verifier from the list of matched credentials.
8. Select **Send Response**.
9. You should be redirected back to the MATTR Labs remote presentation testing tool, where you will
see a successful verification indication.
The result will look something like this:
#### Cross-device workflow [#cross-device-workflow-1]
1. Run the app and then close it (this updates the app on your testing device).
2. Use a desktop browser to navigate to the [MATTR Labs remote presentation testing tool](https://tools.mattrlabs.com/verify-credentials).
3. Select **OID4VP (Redirect)** from the *Select Experience* list.
4. Select **Request credentials**.
5. Open the camera on your testing mobile device and scan the QR code.
6. Confirm opening the QR code with your tutorial application.
7. The tutorial application should be launched on your testing mobile device.
8. Select the credential you wish to send to the verifier from the list of matched credentials.
9. Select **Send Response**.
10. Back on your desktop browser, you should see a successful verification indication.
The result will look something like this:
## Summary [#summary]
You have just used the [mDocs Holder SDKs](/docs/holding/sdk-overview) to build an application
that can present a claimed [mDoc](/docs/concepts/mdocs) to a verifier remotely via an
[online presentation workflow](/docs/verification/remote-overview) as per
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html).
This was achieved by building the following capabilities into the application:
1. Handle an OID4VP [request URI](/docs/verification/remote-overview#authorization-request).
2. Create an online presentation session.
3. Handle a presentation request.
4. Send a presentation response.
## What's next? [#whats-next]
* You can build additional capabilities into your new application:
* Present a claimed mDoc for verification via a
[proximity presentation workflow](/docs/holding/proximity-presentation-tutorial).
* You can [build a web application](/docs/verification/remote-web-verifiers/tutorial) that will interact with your
wallet application via an online verification workflow.
* You can check out the SDKs reference documentation for more details on the available functions and
classes:
* [iOS](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk)
* [Android](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/index.html)
* [React Native](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/index.html)
# Getting started with the Holder SDKs
URL: /docs/holding/sdk-getting-started
Description: Set up access to the MATTR Pi mDocs Holder SDKs, configure SDK tethering, and initialize the SDK in your mobile application.
This guide walks you through the steps required to start building with the MATTR Pi mDocs Holder
SDKs. By the end, your mobile application will be connected to a MATTR VII tenant and ready to
use SDK capabilities such as credential claiming and presentation.
### Request SDK access [#request-sdk-access]
To access the MATTR Pi mDocs Holder SDKs, complete the
[Get Started form](/docs/resources/get-started) with the following details:
* Your organization name and contact information.
* The platform(s) you plan to build for (iOS, Android, or React Native).
* A brief description of your use case.
Once your request is reviewed, you will receive access to the relevant SDK packages and
the MATTR Portal.
### Create a MATTR VII tenant [#create-a-mattr-vii-tenant]
Your application requires a MATTR VII tenant that serves as the backend for SDK operations
including tethering, credential issuance, and verification.
1. Log into the [MATTR Portal](/docs/platform-management/portal).
2. [Create a new tenant](/docs/platform-management/portal#creating-a-tenant) to serve as the
backend for your application.
3. Note the tenant URL (e.g., `https://your-tenant.vii.mattr.global`) — you will need it when
initializing the SDK.
### Create a Holder Application [#create-a-holder-application]
SDK Tethering is optional, but we recommend configuring it so your application can use capabilities
such as Wallet Attestation and so you can view registered app instances from your tenant. To enable
it, create a Holder Application on your MATTR VII tenant for each platform target.
For more details on SDK tethering and the capabilities it enables, see
[SDK Tethering](/docs/holding/sdk-operations/sdk-tethering).
SDK Tethering is optional. To enable it, create a Holder Application on your MATTR VII tenant for
each platform target (iOS and Android). This is a one-time setup process that registers your app
with the tenant and allows app instances to obtain the necessary tokens for authentication and operation.
Make a request of the following structure to create an iOS Holder Application configuration on your MATTR VII tenant:
```http title="Request"
POST /v1/holder/applications
```
```json title="Request body"
{
"name": "My iOS Holder Application",
"clientId": "my-wallet-client",
"type": "ios",
"bundleId": "com.yourcompany.holderapp",
"teamId": "YOUR_APPLE_TEAM_ID",
"maxTimeOfflineInSecs": 864000,
"appAttest": {
"required": true,
"environment": "production"
}
}
```
* `name`: A unique name to identify this Holder Application.
* `clientId`: OAuth 2.0 `client_id` value that the holder application uses when requesting client
attestations. This value is included as the `sub` claim in attestation JWTs and must match the
`client_id` configured by issuers who trust this Holder Application.
* `type`: Must be `ios`.
* `bundleId`: The Bundle ID of your iOS app (must match your Xcode project configuration).
* `teamId`: Your Apple Developer Team ID.
* `maxTimeOfflineInSecs`: Maximum number of seconds the SDK can operate offline before requiring
a new license token. Must be between 1 day (86400) and 30 days (2592000). Defaults to 7 days
(604800).
* `appAttest`: App Attest configuration for the iOS holder application:
* `required`: When `true`, the app instance must provide a valid App Attest attestation during
registration and token renewal. When `false`, the app can fall back to assertion-only
authentication. See [Attestation vs Assertion](/docs/holding/sdk-operations/sdk-tethering#attestation-vs-assertion-fall-back) for more details.
* `environment`: The App Attest environment (`development` or `production`). Apple recommends
using `development` for testing and `production` for distribution builds.
A successful response returns a `201` status code with the created Holder Application:
```json title="Response"
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c", // [!code focus]
"name": "My iOS Holder Application",
"clientId": "my-wallet-client",
"type": "ios",
"bundleId": "com.yourcompany.holderapp",
"teamId": "YOUR_APPLE_TEAM_ID",
"maxTimeOfflineInSecs": 864000,
"appAttest": {
"required": true,
"environment": "production"
}
}
```
* `id`: A unique identifier for the Holder Application (generated by the tenant). You must use this value when initializing the SDK so that it can correctly
identify and authenticate your application.
Make a request of the following structure to create an Android Holder Application configuration on your MATTR VII tenant:
```http title="Request"
POST /v1/holder/applications
```
```json title="Request body"
{
"name": "My Android Holder Application",
"clientId": "my-wallet-client",
"type": "android",
"packageName": "com.yourcompany.holderapp",
"packageSigningCertificateThumbprints": [
"1232584B6F6A892D356899FB9576C5F226A179E6199F2B7A1D837B5C234C5A8E"
],
"maxTimeOfflineInSecs": 864000,
"keyAttestation": {
"required": true
}
}
```
* `name`: A unique name to identify this Holder Application.
* `clientId`: OAuth 2.0 `client_id` value that the holder application uses when requesting client
attestations. This value is included as the `sub` claim in attestation JWTs and must match the
`client_id` configured by Issuers who trust this Holder Application.
* `type`: Must be `android`.
* `packageName`: The package name of your Android application.
* `packageSigningCertificateThumbprints`: SHA-256 hex-encoded fingerprints of the signing key
certificates used to sign your APK or app bundle. This ensures the tenant only accepts requests
from known and trusted applications. Refer to
[Android app signing](/docs/holding/android-app-signing) for more information.
* `maxTimeOfflineInSecs`: Maximum number of seconds the SDK can operate offline before requiring
a new license token. Must be between 1 day (86400) and 30 days (2592000). Defaults to 7 days
(604800).
* `keyAttestation`: Key Attestation configuration for the Android holder application:
* `required`: When `true`, the app instance must provide a valid Key Attestation during
registration and token renewal. When `false`, the app can register and renew tokens using
just an authentication assertion. See [Attestation vs Assertion](/docs/holding/sdk-operations/sdk-tethering#attestation-vs-assertion-fall-back) for more details.
A successful response returns a `201` status code with the created Holder Application:
```json title="Response"
{
"id": "a82bfa46-72a0-4cde-b6cb-2a0de7e2f3c4", // [!code focus]
"name": "My Android Holder Application",
"clientId": "my-wallet-client",
"type": "android",
"packageName": "com.yourcompany.holderapp",
"packageSigningCertificateThumbprints": [
"1232584B6F6A892D356899FB9576C5F226A179E6199F2B7A1D837B5C234C5A8E"
],
"maxTimeOfflineInSecs": 864000,
"keyAttestation": {
"required": true
}
}
```
* `id`: A unique identifier for the Holder Application (generated by the tenant). You must use this value when initializing the SDK so that it can correctly
identify and authenticate your application.
For React Native applications, you must create **both** an iOS and an Android Holder Application
on your MATTR VII tenant, and then conditionally pass the correct configuration based on the
platform OS at runtime.
**Step 1: Create the iOS Holder Application**
Follow the instructions in the **iOS** tab to create a Holder Application configuration for iOS.
**Step 2: Create the Android Holder Application**
Follow the instructions in the **Android** tab to create a Holder Application configuration for Android.
**Step 3: Pass the correct configuration based on Platform OS**
When initializing the SDK, use `Platform.OS` to conditionally provide the matching Holder
Application configuration:
```typescript title="Example"
import { Platform } from "react-native";
const holderApplicationId =
Platform.OS === "ios"
? "YOUR_IOS_HOLDER_APPLICATION_ID"
: "YOUR_ANDROID_HOLDER_APPLICATION_ID";
```
* `YOUR_IOS_HOLDER_APPLICATION_ID` : The `id` returned when you created the iOS Holder Application.
* `YOUR_ANDROID_HOLDER_APPLICATION_ID` : The `id` returned when you created the Android Holder Application.
### Initialize the SDK [#initialize-the-sdk]
If you are using SDK Tethering, update your SDK initialization to include the platform
configuration once your Holder Applications are created. This enables your app to connect to the
correct MATTR VII tenant and Holder Application. If you are not using tethering, you can initialize
the SDK without a `platformConfiguration`.
Initialize the SDK with your platform configuration:
```swift title="Initialization"
let platformConfig = PlatformConfiguration(
tenantHost: URL(string: "https://your-tenant.vii.mattr.global")!,
applicationId: "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
)
try await MobileCredentialHolder.shared.initialize(
platformConfiguration: platformConfig
)
```
* `tenantHost`: The URL of your MATTR VII tenant. This must be the tenant where your iOS Holder
Application is configured.
* `applicationId`: The `id` of your configured iOS Holder Application.
Initialize the SDK with your platform configuration:
```kotlin title="Initialization"
val platformConfig = PlatformConfiguration(
tenantHost = URL("https://your-tenant.vii.mattr.global"),
applicationId = "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
)
MobileCredentialHolder.initialize(context, platformConfiguration = platformConfig)
```
* `tenantHost`: The URL of your MATTR VII tenant where your Android Holder Application is configured.
* `applicationId`: The `id` of your configured Android Holder Application.
Since React Native bridges both iOS and Android, and each platform has its own Holder Application
registered on your MATTR VII tenant (see
[Create a Holder Application](/docs/holding/sdk-getting-started#create-a-holder-application)), your
initialization code must pass the correct platform-specific `applicationId` at runtime. Use
`Platform.OS` to select the appropriate value:
```typescript title="Initialization"
import { initialize } from "@mattrglobal/mobile-credential-holder-react-native";
import { Platform } from "react-native";
const applicationId =
Platform.OS === "android"
? "your-android-holder-application-id"
: "your-ios-holder-application-id";
await initialize({
platformConfiguration: {
tenantHost: "https://your-tenant.vii.mattr.global",
applicationId,
},
});
```
Replace the placeholder values with the `id` returned when you created each Holder Application. In
practice, you would typically store these values in a configuration file or environment variables.
## Next steps [#next-steps]
Your application is now initialized and ready to use the SDK. If you configured a
`platformConfiguration`, it is also tethered to your MATTR VII tenant. Explore the following guides
to start building:
* [Credential claiming tutorial](/docs/holding/credential-claiming-tutorial): Claim a verifiable
credential into your holder app.
* [Remote presentation tutorial](/docs/holding/remote-presentation-tutorial): Present credentials
to a web-based verifier.
* [Proximity presentation tutorial](/docs/holding/proximity-presentation-tutorial): Present
credentials in-person using Bluetooth.
* [SDK Quickstart](/docs/holding/sdk-quickstart): Run a sample holder app end-to-end in 15-20
minutes.
# SDK or ready-made wallet?
URL: /docs/holding/sdk-or-wallet
Before diving into integration, decide which path suits your product strategy. This page compares
embedding the MATTR Pi Holder SDK against deploying the ready-made MATTR GO Hold wallet.
## SDK or ready-made wallet? [#sdk-or-ready-made-wallet]
Before diving into integration, decide which path suits your product strategy.
### MATTR Pi Holder SDK: build it into your app [#mattr-pi-holder-sdk-build-it-into-your-app]
Best for: teams that want credential holding embedded within their existing mobile application, with
full control over the user experience and branding.
The [MATTR Pi mDocs Holder SDK](/docs/holding/sdk-overview) provides native libraries for iOS, Android,
and React Native. You integrate the SDK into your existing codebase, and your app gains the ability to
claim, store, and present credentials directly.
**Choose the SDK when you:**
* Already have a mobile app and want to add credential capabilities to it.
* Need full control over the user interface and interaction flow.
* Want credential holding to be seamless within your existing app experience.
* Need to customize presentation consent screens, credential displays, or notification handling.
* Are building a purpose-specific application (e.g., employee app, government services app).
### MATTR GO Hold: use a ready-made wallet [#mattr-go-hold-use-a-ready-made-wallet]
Best for: teams that want to offer credential holding without building or maintaining a custom wallet,
or that need a wallet for testing and pilot deployments.
[MATTR GO Hold](/docs/holding/go-hold/getting-started) is a downloadable wallet application that
supports credential claiming and presentation out of the box. It can serve as a white-label starting
point or a production wallet for end users.
**Choose MATTR GO Hold when you:**
* Want to get to market quickly without custom mobile development.
* Need a wallet for pilot programs, proofs of concept, or testing.
* Don't require deep integration with an existing app experience.
* Want a standalone wallet app for your users.
* Need a reference implementation to guide your own SDK integration later.
### Comparison [#comparison]
| Consideration | MATTR Pi Holder SDK | MATTR GO Hold |
| ------------------------ | --------------------------------------- | ------------------------------------ |
| Integration effort | Medium–high (native SDK integration) | Low (deploy existing app) |
| UX customization | Full control | Limited to configuration |
| Branding | Your app, your brand | MATTR GO or white-label |
| Platform support | iOS 15+, Android 7+, React Native 0.78+ | iOS 15+, Android 7+ |
| Credential claiming | OID4VCI (Auth Code + Pre-authorized) | OID4VCI (Auth Code + Pre-authorized) |
| Proximity presentation | ISO/IEC 18013-5 via BLE | ISO/IEC 18013-5 via BLE |
| Remote presentation | ISO/IEC 18013-7 + OID4VP | ISO/IEC 18013-7 + OID4VP |
| Time to first credential | Days–weeks (development cycle) | Hours (configuration only) |
## Next steps [#next-steps]
If you are integrating the SDK, review the [platform requirements](/docs/holding/platform-requirements)
next.
# MATTR Pi mDocs Holder SDKs Overview
URL: /docs/holding/sdk-overview
## Overview [#overview]
The mDocs Holder SDKs are based on the [ISO/IEC 18013-5](https://www.iso.org/standard/69084.html)
standard which establishes an interoperable digital representation of mobile-based credentials such
as mobile drivers licenses (mDL). However, these SDKs can extend the same technology and
architecture to more than just mDLs, but rather any conforming mobile document
([mDoc](/docs/concepts/mdocs)) - a term defined in ISO/IEC 18013-5.
The mDocs Holder SDKs are available for React Native, iOS, and Android. They help developers add
mDocs holding features to their apps, allowing users to securely claim and present mDocs in various
interoperable workflows.
To get started with any of our mDocs Holder SDKs, please [contact
us](mailto:sales@mattr.global).
## SDK Capabilities [#sdk-capabilities]
The mDocs Holder SDKs offer tools to assist developers integrating the following capabilities into
their applications:
* **Claim an mDoc**
* Interact with a credential offer and claim an mDoc as per
[OID4VCI (OpenID for Verifiable Credential Issuance)](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html).
The SDK supports both the
[Authorization Code](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-authorization-code-flow)
and
[Pre-authorized Code](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-pre-authorized-code-flow)
flows.
* Manage a list of trusted issuers which mDoc offers can be validated against.
* Store claimed mDocs and manage access to that storage.
* Manage access to device keys which are bound to issued mDocs.
* Use referenced Status lists to check mDocs' [revocation status](/docs/issuance/revocation/overview).
* **Present an mDoc**
* Present a claimed mDoc for verification via a [proximity presentation workflow](/docs/verification/in-person-overview) as per [ISO 18013-5](https://www.iso.org/standard/69084.html).
* Present a claimed mDoc for verification [remotely](/docs/verification/remote-overview) as per [ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and [OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html).
* **iOS 26+**: Present credentials via the Digital Credentials API for a seamless, OS-native experience without launching your app.
* **Claim an mDoc**
* Interact with a credential offer and claim an mDoc as per
[OID4VCI (OpenID for Verifiable Credential Issuance)](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html).
The SDK supports both the
[Authorization Code](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-authorization-code-flow)
and
[Pre-authorized Code](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-pre-authorized-code-flow)
flows.
* Manage a list of trusted issuers which mDoc offers can be validated against.
* Store claimed mDocs and manage access to that storage.
* Manage access to device keys which are bound to issued mDocs.
* Use referenced Status lists to check mDocs' [revocation status](/docs/issuance/revocation/overview).
* **Present an mDoc**
* Present a claimed mDoc for verification via a [proximity presentation workflow](/docs/verification/in-person-overview) as per [ISO 18013-5](https://www.iso.org/standard/69084.html).
* Present a claimed mDoc for verification [remotely](/docs/verification/remote-overview) as per [ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and [OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html).
* **Claim an mDoc**
* Interact with a credential offer and claim an mDoc as per
[OID4VCI (OpenID for Verifiable Credential Issuance)](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html).
The SDK supports the
[Authorization Code](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-authorization-code-flow)
flow.
* Manage a list of trusted issuers which mDoc offers can be validated against.
* Store claimed mDocs and manage access to that storage.
* Manage access to device keys which are bound to issued mDocs.
* Use referenced Status lists to check mDocs' [revocation status](/docs/issuance/revocation/overview).
* **Present an mDoc**
* Present a claimed mDoc for verification via a [proximity presentation workflow](/docs/verification/in-person-overview) as per [ISO 18013-5](https://www.iso.org/standard/69084.html).
* Present a claimed mDoc for verification [remotely](/docs/verification/remote-overview) as per [ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and [OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html).
## Supported features [#supported-features]
### ISO/IEC 18013-5 [#isoiec-18013-5]
Below is a summary of [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html) features
supported by the mDocs Holder SDKs:
| Feature | Supported options | Default |
| :------------------------------ | :--------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------- |
| Device engagement | QR code | QR code |
| Device retrieval data transport | BLE with either `mDoc peripheral server` or `mDoc central client` mode | BLE with `mDoc central client` |
| Ephemeral session key curve | Any NIST P-\* keys | P-256 key using [secure enclave](https://support.apple.com/en-nz/guide/security/sec59b0b31ff/web) |
| IACA public key curves | P-256, P-384, P-521 | Determined by Issuer |
| Device authentication mode | Digital Signature or ECDH-agreed MAC | Digital Signature with a P-256 key using [secure enclave](https://support.apple.com/en-nz/guide/security/sec59b0b31ff/web) |
| Feature | Supported options | Default |
| :------------------------------ | :--------------------------------------------------------------------- | :------------------------------------------------ |
| Device engagement | QR code or NFC | No default; explicit selection required |
| Device retrieval data transport | BLE with either `mDoc peripheral server` or `mDoc central client` mode | BLE with `mDoc central client` |
| Ephemeral session key curve | Any NIST P-\* keys | P-256 key using Keystore |
| IACA public key curves | P-256, P-384, P-521 | Determined by Issuer |
| Device authentication mode | Digital Signature or ECDH-agreed MAC | Digital Signature with a P-256 key using Keystore |
| Feature | Supported options | Default |
| :------------------------------ | :--------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Device engagement | QR code | QR code |
| Device retrieval data transport | BLE with either `mDoc peripheral server` or `mDoc central client` mode | BLE with `mDoc peripheral server` |
| Ephemeral session key curve | Any NIST P-\* keys | P-256 key using [secure enclave](https://support.apple.com/en-nz/guide/security/sec59b0b31ff/web) (iOS) / Keystore (Android) |
| IACA public key curves | P-256, P-384, P-521 | Determined by Issuer |
| Device authentication mode | Digital Signature or ECDH-agreed MAC | Digital Signature with a P-256 key using [secure enclave](https://support.apple.com/en-nz/guide/security/sec59b0b31ff/web) (iOS) / Keystore (Android) |
### ISO/IEC 18013-7 [#isoiec-18013-7]
Below is a summary of [ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) features
supported by the mDocs Holder SDKs:
| Feature | Options Supported | Default Option Selected |
| --------------------------------- | ------------------------------------------------------- | --------------------------------- |
| Data Retrieval methods | OID4VP | OID4VP |
| Wallet Invocation | Custom URL and QR Code-based | Both |
| MDoc Reader validation | Stored verifier certificates, client Metadata retrieval | Both |
| Authorization Response Encryption | ECDH in Direct Key Agreement mode | ECDH in Direct Key Agreement mode |
| Feature | Options Supported | Default Option Selected |
| --------------------------------- | ------------------------------------------------------- | --------------------------------- |
| Data Retrieval methods | OID4VP | OID4VP |
| Wallet Invocation | Custom URL and QR Code-based | Both |
| MDoc Reader validation | Stored verifier certificates, client Metadata retrieval | Both |
| Authorization Response Encryption | ECDH in Direct Key Agreement mode | ECDH in Direct Key Agreement mode |
| Feature | Options Supported | Default Option Selected |
| --------------------------------- | ------------------------------------------------------- | --------------------------------- |
| Data Retrieval methods | OID4VP | OID4VP |
| Wallet Invocation | Custom URL and QR Code-based | Both |
| MDoc Reader validation | Stored verifier certificates, client Metadata retrieval | Both |
| Authorization Response Encryption | ECDH in Direct Key Agreement mode | ECDH in Direct Key Agreement mode |
## System requirements [#system-requirements]
The iOS mDocs Holder SDK is developed in the [Swift](https://developer.apple.com/swift/) programming
language and is meant for integration into iOS applications developed in Swift and/or Objective-C.
Specifically it currently only supports applications developed in iOS 15 and above.
**Digital Credentials API support (iOS 26+)**: The SDK supports the Digital Credentials API for presenting credentials via an OS-native overlay without launching your app. This feature requires iOS 26 or later.
This SDK is developed in the [Kotlin](https://kotlinlang.org/) programming language and is meant for
integration into Android applications. It currently supports Android 7 (API level 24) and above.
This SDK is meant for integration into [React Native](https://reactnative.dev/) applications using
React Native 0.78 and above. Supported operating systems are:
* iOS 15 or higher.
* Android 7 or higher.
## Dependencies [#dependencies]
This section lists all dependencies for using mDocs Holder SDKs.
**Third party dependencies**
* [CBORCoding](https://github.com/SomeRandomiOSDev/CBORCoding) (MIT license)
* [swift-certificates](https://github.com/apple/swift-certificates.git) 1.7.0 (Apache-2.0 License)
* [swift-asn1](https://github.com/apple/swift-asn1) 1.3.1 (Apache-2.0 License)
**Apple frameworks**
* [Security](https://developer.apple.com/documentation/security)
* [CryptoKit](https://developer.apple.com/documentation/cryptokit/)
* [LocalAuthentication](https://developer.apple.com/documentation/localauthentication)
* [AuthenticationServices](https://developer.apple.com/documentation/authenticationservices)
* [CoreBluetooth](https://developer.apple.com/documentation/corebluetooth)
* [Combine](https://developer.apple.com/documentation/combine)
* [OSLog](https://developer.apple.com/documentation/oslog)
* [UIKit](https://developer.apple.com/documentation/uikit)
* [AppKit (on macOS)](https://developer.apple.com/documentation/appkit)
* [IdentityDocumentServices](https://developer.apple.com/documentation/IdentityDocumentServices)
* [IdentityDocumentServicesUI](https://developer.apple.com/documentation/IdentityDocumentServicesUI)
**Toolchain dependencies**
* Swift 5.10.
* iOS support: The SDK functionality is only available for devices from iOS 15 onwards.
* Xcode: The SDK was built with the latest stable Xcode available in GitHub Actions (setup-xcode action with latest-stable label), and the current version is Xcode 26.5 (17F42).
* macOS 15.
**Kotlin, AGP, Gradle, and Android Studio**
The Android Holder SDK is built using Kotlin 2.0. This adds some intrinsic dependencies into your build tools.
* Kotlin 2.0 is supported from AGP version [8.5](https://developer.android.com/build/kotlin-support).
* AGP 8.5 is supported from Gradle version [8.7](https://developer.android.com/build/releases/about-agp) and Android Studio Koala [2024.1.1](https://developer.android.com/studio/releases).
**Runtime**
A list of runtime dependencies and licenses is generated at build time and packaged in `res/raw/dependencies_licenses.html`. The majority are Kotlin and Android, the rest are listed below:
* [CBOR-Java](https://github.com/peteroupc/CBOR-Java) - [Public Domain](https://github.com/peteroupc/CBOR-Java/blob/master/LICENSE.md)
* [Timber](https://github.com/JakeWharton/timber) - [Apache 2.0](https://github.com/JakeWharton/timber/blob/trunk/LICENSE.txt)
**Standard libraries**
* [androidx.activity:activity-ktx:1.9.0](https://developer.android.com/jetpack/androidx/releases/activity#1.9.0)
* [androidx.annotation:annotation:1.8.1](https://developer.android.com/jetpack/androidx/releases/annotation#1.8.1)
* [androidx.appcompat:appcompat:1.7.0](https://developer.android.com/jetpack/androidx/releases/appcompat#1.7.0)
* [androidx.biometric:biometric-ktx:1.2.0-alpha05](https://developer.android.com/jetpack/androidx/releases/biometric#1.2.0-alpha05)
* [androidx.browser:browser:1.8.0](https://developer.android.com/jetpack/androidx/releases/browser#1.8.0)
* [androidx.core:core-ktx:1.15.0](https://developer.android.com/jetpack/androidx/releases/core#1.15.0)
* [androidx.credentials:credentials-play-services-auth:1.5.0](https://developer.android.com/jetpack/androidx/releases/credentials#1.5.0)
* [androidx.credentials:credentials:1.5.0](https://developer.android.com/jetpack/androidx/releases/credentials#1.5.0)
* [androidx.fragment:fragment:1.5.7](https://developer.android.com/jetpack/androidx/releases/fragment#1.5.7)
* [org.jetbrains.kotlin:kotlin-reflect:1.9.22](https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-reflect/1.9.22)
* [org.jetbrains.kotlin:kotlin-stdlib:2.0.0](https://kotlinlang.org/)
* [org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3](https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.7.3)
* [org.jetbrains.kotlinx:kotlinx-datetime:0.4.0](https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-datetime-jvm/0.4.0)
* [org.jetbrains.kotlinx:kotlinx-io-bytestring:0.6.0](https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-io-bytestring-tvosarm64/0.6.0)
* [org.jetbrains.kotlinx:kotlinx-io-core:0.6.0](https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-io-core/0.6.0)
* [org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3](https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-serialization-json/1.6.3)
**Third-party libraries**
* [com.jakewharton.timber:timber:5.0.1](https://mvnrepository.com/artifact/com.jakewharton.timber/timber/5.0.1)
* [com.upokecenter:cbor:4.5.2](https://mvnrepository.com/artifact/com.upokecenter/cbor/4.5.2)
* None.
## Versions [#versions]
Below are the available versions of the mDocs Holder SDKs, including the current active version,
supported versions, and those that have reached end-of-life (EOL).
| Major version | Status | Latest release | End of Life date | Documentation |
| ------------- | ----------- | -------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| v6 | Active | 6.0.0 | - | [SDK Docs](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/6.0.0/documentation/mobilecredentialholdersdk/) |
| v5 | Maintenance | 5.2.0 | 22-09-2026 | [SDK Docs](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/5.2.0/documentation/mobilecredentialholdersdk/) |
| v4 | End of Life | 4.4.0 | 13-05-2026 | - |
| v3 | End of Life | 3.0.0 | 7-10-2025 | - |
| v2 | End of Life | 2.0.0 | 26-08-2025 | - |
| v1 | End of Life | 1.0.0 | 05-05-2025 | - |
| Major version | Status | Latest release | End of Life date | Documentation |
| ------------- | ----------- | -------------- | ---------------- | ------------------------------------------------------------------------------------------ |
| v7 | Active | 7.0.0 | - | [SDK Docs](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/7.0.0/) |
| v6 | Maintenance | 6.1.4 | 22-09-2026 | [SDK Docs](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/6.1.4/) |
| v5 | End of Life | 5.3.2 | 13-05-2026 | - |
| v4 | End of Life | 4.1.1 | 30-12-2025 | - |
| v3 | End of Life | 3.0.0 | 7-10-2025 | - |
| v2 | End of Life | 2.0.0 | 26-08-2025 | - |
| v1 | End of Life | 1.1.0 | 05-05-2025 | - |
| Major version | Status | Latest release | End of Life date | Documentation |
| ------------- | ----------- | -------------- | ---------------- | --------------------------------------------------------------------------------------------------------- |
| v9 | Active | 9.0.4 | - | [SDK Docs](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/9.0.4/index.html) |
| v8 | End of Life | 8.1.3 | 23-06-2026 | - |
| v7 | End of Life | 7.0.0 | 12-12-2025 | - |
| v6 | End of Life | 6.0.0 | 19-11-2025 | - |
Release candidates (RC) are pre-release versions that may contain new features or changes that are
not yet fully tested. They are intended for testing purposes, are not subject to our SLA and should
not be used in production environments.
# Holder SDK quickstart guide
URL: /docs/holding/sdk-quickstart
This quickstart is for evaluating the [MATTR Pi mDocs Holder SDKs](/docs/holding/sdk-overview). In about 15-20 minutes you will configure a sample holder mobile app (iOS, Android, or React Native), run it on a physical device and use it to:
1. **Claim a credential** issued through an OID4VCI workflow.
2. **Present that credential remotely** to a web verifier application.
3. **Present that credential in-person** to a verifier application on a different mobile device.
**Estimated time: 15-20 minutes.**
Use this guide as a fast evaluation path to see the Holder SDK working
end-to-end on a real device. For detailed information and API examples,
explore the individual tutorials and reference documentation for [credential
claiming](/docs/holding/credential-claiming-tutorial), [remote
presentation](/docs/holding/remote-presentation-tutorial), and [proximity
presentation](/docs/holding/proximity-presentation-tutorial).
## Prerequisites [#prerequisites]
* Access to the MATTR Pi mDocs Holder SDK for your chosen platform (iOS, Android, or React Native). Apply for access [here](/docs/resources/get-started).
* A physical mobile device to run the holder application:
* iOS/Android mobile device set up with biometric authentication and Bluetooth access.
* Development environment set up for your chosen platform (e.g., Xcode for iOS, Android Studio for Android, or a React Native development environment).
* Install the **MATTR GO Verify example app** on a **separate** mobile device for [iOS](https://apps.apple.com/us/app/mattr-go-verify/id6670461328) or [Android](https://play.google.com/store/apps/details?id=global.mattr.mobile.verifier). This is used for in-person verification testing.
## Evaluation Steps [#evaluation-steps]
In this quickstart you will:
1. Configure and run a local sample holder project for your platform.
2. Claim a credential into the sample holder app.
3. Present that credential remotely to a web verifier.
4. Present that credential in-person to a verifier app on a different device.
Use the tabs below to follow platform-specific setup steps.
### Part 1: Configure the sample holder project (5-10 minutes) [#part-1-configure-the-sample-holder-project-5-10-minutes]
1. Access the sample holder codebase by either:
* Cloning the [MATTR Sample Apps repository](https://github.com/mattrglobal/sample-apps):
```bash title="Clone sample apps"
git clone https://github.com/mattrglobal/sample-apps.git
```
* Or downloading just the iOS sample holder project using [download-directory.github.io](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2Fmattrglobal%2Fsample-apps%2Ftree%2Fmaster%2Fios-holder-tutorial-sample-app).
2. Use Xcode to open the `.xcodeproj` file in the project's folder. You can find it in the `sample-apps/ios-holder-tutorial-sample-app` directory.
3. Drag the `MobileCredentialHolderSDK-*version*.xcframework` folder (obtained from MATTR as part of the SDK package) into your project.
4. Configure `MobileCredentialHolderSDK-*version*.xcframework` to [Embed & sign](https://help.apple.com/xcode/mac/current/#/dev51a648b07).
5. Set a unique bundle identifier for the project in the Xcode project settings (e.g., `com.yourname.holdersample`).
6. Run the project on a physical iOS device (simulators do not support the required Bluetooth capabilities).
1. Access the sample holder codebase by either:
* Cloning the [MATTR Sample Apps repository](https://github.com/mattrglobal/sample-apps):
```bash title="Clone sample apps"
git clone https://github.com/mattrglobal/sample-apps.git
```
* Or downloading just the Android sample holder project using [download-directory.github.io](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2Fmattrglobal%2Fsample-apps%2Ftree%2Fmaster%2Fandroid-holder-tutorial-sample-app).
2. Open the project in Android Studio. You can find it in the `sample-apps/android-holder-tutorial-sample-app` directory.
3. Unzip the `mobile-credential-holder-*version*.zip` file (obtained from MATTR as part of the SDK package).
4. Drag the unzipped `global` folder into the project's `repo` folder.
5. Sync Project with Gradle files to recognize the new module.
6. Run the project on a physical Android device (emulators do not support the required Bluetooth capabilities).
1. Access the sample holder codebase by either:
* Cloning the [MATTR Sample Apps repository](https://github.com/mattrglobal/sample-apps):
```bash title="Clone sample apps"
git clone https://github.com/mattrglobal/sample-apps.git
```
* Or downloading just the React Native sample holder project using [download-directory.github.io](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2Fmattrglobal%2Fsample-apps%2Ftree%2Fmaster%2Freact-native-mdocs-holder-tutorial%2Freact-native-mdocs-holder-tutorial-complete).
2. Open the completed sample holder project in your code editor. You can find it in the
`sample-apps/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-complete`
directory.
3. Open the `app.config.ts` file.
4. Update the iOS `bundleIdentifier` to a unique value for your application, e.g. `com.mycompany.myapp`.
5. Update the Android `package` to a unique value, e.g. `com.mycompany.myapp`.
6. Navigate to the project directory and install dependencies:
```bash title="Install dependencies"
yarn install
```
You must be logged in to npm with access to the MATTR Pi mDocs Holder SDK
package to install dependencies successfully. If you have not yet been granted
access to the SDK, apply for access [here](/docs/resources/get-started).
7. Run the following command to generate the iOS and Android project files:
```bash title="Generate native project files"
npx expo prebuild
```
You should now see the `ios` and `android` folders in your project root.
8. Connect your testing device and run the following command to start the application:
**iOS**
```bash title="Run iOS application"
yarn ios --device
```
**Android**
```bash title="Run Android application"
yarn android --device
```
### Part 2: Claim a credential (3 minutes) [#part-2-claim-a-credential-3-minutes]
Use the sample holder app you just built to claim a credential from a MATTR VII tenant. This credential will be used in the subsequent presentation steps.
1. Open the MATTR Labs [Pre-authorized Offer tool](https://tools.mattrlabs.com/issue-credential).
2. Select the **Generate Credential Offer** button.\
A QR code will be generated and rendered on the screen, along with a transaction code.
3. Run your application.
4. Select the **Claim Credential** button.
5. Scan the QR code.
6. Follow any on-screen instructions to claim the credential.
Under the hood, the sample app uses the Holder SDK to discover the credential offer from the QR code, and then claim and store the credential on the device using the OID4VCI [Pre-authorized Code flow](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-pre-authorized-code-flow).
### Part 3: Present the credential remotely (5 minutes) [#part-3-present-the-credential-remotely-5-minutes]
Present the credential you just claimed to a web verifier application using the [remote presentation workflow](/docs/verification/remote-overview) defined by [ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html).
1. Use a **mobile browser on the device** where the sample application is installed to navigate to the
[MATTR Labs remote presentation testing tool](https://tools.mattrlabs.com/mdoc-online-presentation).
2. Select the **OID4VP (Redirect)** option from the *Select Experience* list.
3. Select **Request credentials**.
4. Select **Allow** to open the sample application.
5. Select the credential you claimed in the previous part.
6. Select **Send Response**.
7. You should be redirected back to the MATTR Labs remote presentation testing tool, where you will
see a successful verification indication.
1. Use a **desktop browser** to navigate to the [MATTR Labs remote presentation testing tool](https://tools.mattrlabs.com/verify-credentials).
2. Select **OID4VP (Redirect)** from the *Select Experience* list.
3. Select **Request credentials**.
4. Open the camera on the device where the sample application is installed and scan the QR code.
5. Confirm opening the QR code with your sample application (this message might vary depending on your device and platform).
6. Select the credential you claimed in the previous part.
7. Select **Send Response**.
8. Back on your desktop browser, you should see a successful verification indication.
In these flows, the sample app uses the Holder SDK to respond to the OID4VP authorization request, as defined in the OID4VP specification and ISO/IEC 18013-7:2025.
### Part 4: Present the credential in-person (3 minutes) [#part-4-present-the-credential-in-person-3-minutes]
Present the credential you claimed to a verifier application on a different mobile device using the [proximity presentation workflow](/docs/verification/in-person-overview) as defined by [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
Once you have the sample holder app running on one device, and the MATTR GO Verify app installed on a separate device, follow these steps to test in-person presentation:
1. On the **sample holder app**, select **Present Credentials**.
2. The app will display a QR code containing the device engagement string.
3. On the **verifier device**, launch the MATTR GO Verify app, select **Verify** and scan the QR code from the holder app.
4. On the **sample holder app**, review the verifier's request and consent to share the credential.
5. The verifier application will display the verification results.
Behind the scenes, the Holder SDK handled device engagement, established a secure BLE session, and presented the mDoc to the verifier as per ISO/IEC 18013-5:2021.
## Review the codebase [#review-the-codebase]
The sample holder application uses the [Holder SDK](/docs/holding/sdk-overview) to claim and present mDocs. Once you have the sample application running, use this section to understand the key SDK calls you will reuse in your own application.
### Initialize the SDK [#initialize-the-sdk]
```swift title="Initialize the SDK"
try await mobileCredentialHolder.initialize(
userAuthenticationConfiguration: UserAuthenticationConfiguration( // [!code highlight]
userAuthenticationBehavior: .always, // [!code highlight]
userAuthenticationType: .biometricOrPasscode, // [!code highlight]
presentationTimeoutSeconds: 20 // [!code highlight]
),
credentialIssuanceConfiguration: CredentialIssuanceConfiguration(
redirectUri: "", // [!code highlight]
autoTrustMobileCredentialIaca: true // [!code highlight]
),
dcConfiguration: DCConfiguration( // iOS 26+ only // [!code highlight]
appGroup: "group.com.yourcompany.app", // [!code highlight]
supportedDocTypes: [.mDL, .eudi] // [!code highlight]
) // [!code highlight]
)
```
* `userAuthenticationConfiguration`: Optional. Configures when the SDK requires user authentication
to access or present credentials. The example shows the default values. Refer to the
[reference documentation](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/userauthenticationconfiguration)
for additional details about the available configuration options.
* `redirectUri` : Provide a URL that corresponds to a specific path in your application. The SDK
will redirect the user to this path after they successfully complete authentication. Required for
the
[`Authorization Code flow`](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-authorization-code-flow).
* `autoTrustMobileCredentialIaca` : Set to `true` if you trust the credential issuer(s) for any
offer. Defaults to `false`.
* `dcConfiguration`: **Optional, iOS 26+ only**. Configures Digital Credentials API support for presenting credentials via an OS-native overlay. Specify your app group and supported document types (e.g., `.mDL`, `.eudi`, `.euav`, `.photoid`, `.jpMnc`).
```kotlin title="Initialize the SDK"
mobileCredentialHolder.initialize(
activity,
userAuthenticationConfiguration = UserAuthenticationConfiguration( // [!code highlight]
behavior = UserAuthenticationBehavior.Always, // [!code highlight]
presentationTimeoutSeconds = 20 // [!code highlight]
),
credentialIssuanceConfiguration = CredentialIssuanceConfiguration(
redirectUri = "", // [!code highlight]
autoTrustMobileCredentialIaca = true // [!code highlight]
)
)
```
* `userAuthenticationConfiguration`: Optional. Configures when the SDK requires user authentication
to access or present credentials. The example shows the default values. Refer to the
[reference documentation](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-user-authentication-configuration/index.html)
for additional details about the available configuration options.
* `redirectUri` : Provide a URL that corresponds to a specific path in your application. The SDK
will redirect the user to this path after they successfully complete authentication. Required for
the
[`Authorization Code flow`](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-authorization-code-flow).
* `autoTrustMobileCredentialIaca` : Set to `true` if you trust the credential issuer(s) for any
offer. Defaults to `false`.
```ts title="Initialize the SDK"
const initializeResult = await mobileCredentialHolder.initialize({
userAuthenticationConfiguration: {
// [!code highlight]
userAuthenticationBehavior: UserAuthenticationBehavior.OnInitialize, // [!code highlight]
userAuthenticationType: UserAuthenticationType.BiometricOrPasscode, // [!code highlight]
},
credentialIssuanceConfiguration: {
// [!code highlight]
autoTrustMobileCredentialIaca: true, // [!code highlight]
redirectUri: "", // [!code highlight]
},
});
if (initializeResult.isErr()) {
const { error } = initializeResult;
// handle error scenarios
return;
}
```
* `userAuthenticationConfiguration`: Optional. Configures when the SDK requires user authentication
to access or present credentials. Refer to the
[reference documentation](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/types/UserAuthenticationConfiguration.html)
for additional details about the available configuration options.
* `redirectUri`: Provide a URL that corresponds to a specific path in your application. The SDK
will redirect the user to this path after they successfully complete authentication. Required for
the
[`Authorization Code flow`](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-authorization-code-flow).
* `autoTrustMobileCredentialIaca`: Set to `true` if you trust the credential issuer(s) for any
offer. Defaults to `false`.
### Credential claiming workflow [#credential-claiming-workflow]
#### Discover credential offer [#discover-credential-offer]
```swift title="Discover credential offer"
let discoveredOffer = try await mobileCredentialHolder.discoverCredentialOffer(offerUrl: String)
```
* `offerUrl` : Pass the OID4VCI initiation URL.
The
[`discoverCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/discovercredentialoffer\(_:\))
method returns a
[`DiscoveredCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/discoveredcredentialoffer)
object containing the offer details.
```kotlin title="Discover credential offer"
val discoveredOffer = mobileCredentialHolder.discoverCredentialOffer(offerUri)
```
* `offerUrl` : Pass the OID4VCI initiation URL as a string.
The
[`discoverCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/discover-credential-offer.html?query=suspend%20fun%20discoverCredentialOffer\(offer:%20String\):%20DiscoveredCredentialOffer)
method returns an instance of
[`DiscoveredCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.issuance.dto/-discovered-credential-offer/index.html)
containing the offer details.
```ts title="Discover credential offer"
const discoveredOfferResult =
await mobileCredentialHolder.discoverCredentialOffer(offerUrl); // [!code highlight]
if (discoveredOfferResult.isErr()) {
const { error } = discoveredOfferResult;
// handle error scenarios
return;
}
const credentialOffer = discoveredOfferResult.value;
```
* `offerUrl` : Pass the OID4VCI initiation URL.
The `discoverCredentialOffer` method returns a `Result<`CredentialOfferResponse`, Error>` (using the
[`neverthrow`](https://www.npmjs.com/package/neverthrow) pattern) object containing the offer
details and/or any errors.
#### Claim and store credentials [#claim-and-store-credentials]
```swift title="Claim credentials"
let retrievedCredentialResults = try await mobileCredentialHolder.retrieveCredentials(
credentialOffer: String, // [!code highlight]
clientId: "", // [!code highlight]
transactionCode: nil // [!code highlight]
)
```
* `credentialOffer` : Pass the OID4VCI credential offer URI.
* `clientId` : Pass a string that identifies your wallet application with the issuer.
* `transactionCode` : Provide a transaction code if the
[`Pre-Authorization Code flow`](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-pre-authorized-code-flow)
offer requires a transaction code.
The
[`retrieveCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/retrievecredentials\(credentialoffer:clientid:transactioncode:\))
method returns an array of
[`RetrieveCredentialResult`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/retrievecredentialresult)
objects, each containing the metadata of the retrieved credentials, including the credential ID
(`credentialId`).
```kotlin title="Claim credentials"
val credentialResults = mobileCredentialHolder.retrieveCredentials(
activity = activity,
credentialOffer = offerUri, // [!code highlight]
clientId = "", // [!code highlight]
transactionCode = null // [!code highlight]
)
```
* `credentialOffer` : Pass the OID4VCI credential offer URI.
* `clientId` : Pass a string that identifies your wallet application with the issuer.
* `transactionCode` : Provide a transaction code if claiming a credential using a
[`Pre-authorized Code flow`](/docs/issuance/pre-authorized-code/overview) that requires a
transaction code.
The
[`retrieveCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/retrieve-credentials.html?query=suspend%20fun%20retrieveCredentials\(activity:%20Activity,%20credentialOffer:%20String,%20clientId:%20String,%20transactionCode:%20String?%20=%20null\):%20List%3CRetrieveCredentialResult%3E)
method returns an array of
[`RetrieveCredentialResult`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.issuance.dto/-retrieve-credential-result/index.html)
objects, each containing the metadata of the retrieved credentials, including the credential ID
(`credentialId`).
```ts title="Claim credentials"
const retrievedCredentialsResults =
await mobileCredentialHolder.retrieveCredentials({
credentialOffer: offerUrl, // [!code highlight]
clientId: "", // [!code highlight]
transactionCode: undefined, // [!code highlight]
});
if (retrievedCredentialsResults.isErr()) {
const { error } = retrievedCredentialsResults;
// handle error scenarios
return;
}
const retrievedCredentials = retrievedCredentialsResults.value;
const retrievedCredential = retrievedCredentials[0];
if (!retrievedCredential?.isSuccess) {
// handle error scenarios (retrievedCredential.error / retrievedCredential.docType)
return;
}
const credentialId = retrievedCredential.credentialId;
```
* `credentialOffer`: Pass the OID4VCI credential offer URI.
* `clientId`: Pass a string that identifies your wallet application with the issuer.
* `transactionCode`: Provide a transaction code if the
[`Pre-Authorization Code flow`](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-pre-authorized-code-flow)
offer requires a transaction code.
The `retrieveCredentials` method returns a `Result` (using the
[`neverthrow`](https://www.npmjs.com/package/neverthrow) pattern) object containing the metadata of
the retrieved credentials, including the credential ID (`credentialId`).
#### Access claimed credentials [#access-claimed-credentials]
```swift title="Access credential"
let credential = try mobileCredentialHolder.getCredential(credentialId: String)
```
* `credentialId` : Pass the credential ID of the credential you want to access.
```kotlin title="Access credential"
val credential = mobileCredentialHolder.getCredential(credentialId)
```
* `credentialId` : Pass the credential ID of the credential you want to access, as a string.
```tsx title="Access credential"
const credentialResult =
await mobileCredentialHolder.getCredential(credentialId); // [!code highlight]
if (credentialResult.isErr()) {
const { error } = credentialResult;
// handle error scenarios
return;
}
const credential = credentialResult.value;
```
* `credentialId` : Pass the credential ID of the credential you want to access.
### Remote presentation handling [#remote-presentation-handling]
#### Register the verifier's Authorization URL [#register-the-verifiers-authorization-url]
Ensure that your application is registered to
[handle the verifier's authorization URL](/docs/holding/remote-presentation-tutorial#step-1-register-the-verifiers-authorization-endpoint).
This is typically done in the app's `Info.plist` file. The URL scheme should match the one used by
the verifier.
Ensure that your application is registered to
[handle the verifier's authorization URL](/docs/holding/remote-presentation-tutorial#register-the-verifiers-authorization-endpoint).
This is typically done in the AndroidManifest.xml file. The URL scheme should match the one used by
the verifier.
Ensure that your application is registered to handle the verifier's authorization URL. The URL
scheme should match the one used by the verifier.
* For
[iOS](/docs/holding/remote-presentation-tutorial#step-1-register-the-verifiers-authorization-endpoint)
this is typically done in the app's `Info.plist` file.
* For
[Android](/docs/holding/remote-presentation-tutorial#register-the-verifiers-authorization-endpoint)
this is typically done in the `AndroidManifest.xml` file.
#### Create an online presentation session [#create-an-online-presentation-session]
```swift title="Create an online presentation session"
let onlinePresentationSession = try await mobileCredentialHolder.createOnlinePresentationSession(authorizationRequestUri: authorizationRequestURI, requireTrustedVerifier: false)
```
* `authorizationRequestURI` : Pass the OID4VP authorization request URI as a string.
* `requireTrustedVerifier` : Set to `true` if you want to ensure that the verifier exists on the
app's trusted verifier's list.
The
[`createOnlinePresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/createonlinepresentationsession\(authorizationrequesturi:requiretrustedverifier:deviceauthenticationoption:\))
method returns an instance of
[`OnlinePresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/onlinepresentationsession)
containing requested and matching credentials.
```kotlin title="Create an online presentation session"
val onlinePresentationSession = mobileCredentialHolder.createOnlinePresentationSession(
authorisationRequestUri = authorizationRequestUri,
requireTrustedVerifier = false
)
```
* `authorisationRequestURI` : Pass the OID4VP authorization request URI as a string.
* `requireTrustedVerifier` : Set to `true` if you want to ensure that the verifier exists on the
app's trusted verifier's list.
The
[`createOnlinePresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/create-online-presentation-session.html?query=fun%20createOnlinePresentationSession\(authorisationRequestUri:%20String,%20requireTrustedVerifier:%20Boolean%20=%20false\):%20OnlinePresentationSession)
method returns an instance of
[`OnlinePresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.onlinepresentation/-online-presentation-session/index.html)
containing requested and matching credentials.
```ts title="Create an online presentation session"
const createSessionResult =
await mobileCredentialHolder.createOnlinePresentationSession({
authorizationRequestUri: authorizationRequestURI,
requireTrustedVerifier: false,
});
if (createSessionResult.isErr()) {
const { error } = createSessionResult;
// handle error scenarios
return;
}
const onlinePresentationSession = createSessionResult.value;
```
* `authorizationRequestUri` : Pass the OID4VP authorization request URI as a string.
* `requireTrustedVerifier` : Set to `true` if you want to ensure that the verifier exists on the
app's trusted verifier's list.
The `createOnlinePresentationSession` method returns an instance of `OnlinePresentationSession`
containing requested and matching credentials.
#### Present matching credentials to holder [#present-matching-credentials-to-holder]
```swift title="Present matching credentials to the holder"
let credential = try await mobileCredentialHolder.getCredential(credentialId: id)
```
* `id` : Pass the identifier of the credential you want to present, as a string. This information
can be retrieved from the
[`OnlinePresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/onlinepresentationsession)
object returned by the
[`createOnlinePresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/createonlinepresentationsession\(authorisationrequesturi:requiretrustedverifier:\))
method.
Your application must then gather the user's consent to share the matching credential with the
verifier.
```kotlin title="Present matching credentials to the holder"
val credential = mobileCredentialHolder.getCredential(credentialId)
```
* `credentialId` : Pass the identifier of the credential you want to present, as a string. This
information can be retrieved from the
[`OnlinePresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.onlinepresentation/-online-presentation-session/index.html)
object returned by the
[`createOnlinePresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/create-online-presentation-session.html?query=fun%20createOnlinePresentationSession\(authorisationRequestUri:%20String,%20requireTrustedVerifier:%20Boolean%20=%20false\):%20OnlinePresentationSession)
method.
Your application must then gather the user's consent to share the matching credential with the
verifier.
```ts title="Present matching credentials to the holder"
const credentialResult = await mobileCredentialHolder.getCredential(
credentialId, // [!code highlight]
);
if (credentialResult.isErr()) {
const { error } = credentialResult;
// handle error scenarios
return;
}
const credential = credentialResult.value;
```
* `credentialId` : Pass the identifier of the credential you want to present, as a string. This
information can be retrieved from the `OnlinePresentationSession` object returned by the method.
Your application must then gather the user's consent to share the matching credential with the
verifier.
#### Send a response to the verifier [#send-a-response-to-the-verifier]
```swift title="Send response to the verifier"
try await onlinePresentationSession.sendResponse(credentialIds: [id])
```
* `id` : Pass the identifier of the credential you want to send, as a string.
```kotlin title="Send response to the verifier"
onlinePresentationSession.sendResponse(
credentialIds = listOf(credentialId), // [!code highlight]
activity = activity
)
```
* `credentialId` : Pass the identifier of the credential you want to send, as a string.
```ts title="Send response to the verifier"
const sendResponseResult = await onlinePresentationSession.sendResponse({
credentialIds: [credentialId],
});
if (sendResponseResult.isErr()) {
const { error } = sendResponseResult;
// handle error scenarios
return;
}
```
* `credentialId` : Pass the identifier of the credential you want to send, as a string.
### Proximity presentation handling [#proximity-presentation-handling]
#### Create a proximity presentation session [#create-a-proximity-presentation-session]
```swift title="Create a proximity presentation session"
let proximityPresentationSession = try await mobileCredentialHolder.createProximityPresentationSession(
onRequestReceived: onRequestReceived(_:error:)
// 1. Callback triggered when the verifier requests a credential for verification. Use this callback to display the request on the UI and get the user's consent to share any matched credentials.
)
```
The
[`createProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/createproximitypresentationsession\(onrequestreceived:onconnected:onsessionterminated:deviceauthenticationoption:blemode:\))
method returns an instance of
[`ProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/proximitypresentationsession)
which contains a `deviceEngagement` property that must be shared with the verifier embedded in a QR
code to [establish a secure connection](/docs/verification/in-person-overview#engagement-phase).
```kotlin title="Create a proximity presentation session"
val proximityPresentationSession = mobileCredentialHolder.createProximityPresentationSession(
onRequestReceived = { session, matchedCredentials, error ->
if (error != null) {
// Handle error
} else {
// 1. Show request on UI
// 2. Show matched credentials on UI and get user's consent to share them
// 3:
session.sendResponse(
val credentialIds = matchedCredentials!!.flatMap {
it.matchedCredentials.map { credential -> credential.id }
} ,
activity = activity
)
}
}
)
```
The
[`createProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/create-proximity-presentation-session.html?query=suspend%20fun%20createProximityPresentationSession\(activity:%20Activity,%20deviceAuthenticationOption:%20DeviceAuthenticationOption%20=%20DeviceAuthenticationOption.Signature,%20bleMode:%20BleMode%20=%20BleMode.MDocClientCentral,%20onRequestReceived:%20ProximityPresentationSession.OnRequestReceived,%20onConnected:%20ProximityPresentationSession.OnConnected?%20=%20null,%20onSessionTerminated:%20ProximityPresentationSession.OnSessionTerminated?%20=%20null\):%20ProximityPresentationSession)
method returns an instance of
[`ProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-proximity-presentation-session/index.html)
which contains a `deviceEngagement` property that must be shared with the verifier embedded in a QR
code to [establish a secure connection](/docs/verification/in-person-overview#engagement-phase).
```ts title="Create a proximity presentation session"
const createSessionResult =
await mobileCredentialHolder.createProximityPresentationSession({
onRequestReceived: (data) => {
if ("error" in data) {
// handle error scenarios
return;
}
// 1. Show UI prompt and gather user consent
// 2. Retrieve matching credential from request object
},
});
if (createSessionResult.isErr()) {
const { error } = createSessionResult;
// handle error scenarios
return;
}
const proximityPresentationSession = createSessionResult.value;
```
The `createProximityPresentationSession` method returns an instance of
`ProximityPresentationSession` which contains a `deviceEngagement` property that must be shared with
the verifier embedded in a QR code to
[establish a secure connection](/docs/verification/in-person-overview#engagement-phase).
#### Present matching credentials to holder [#present-matching-credentials-to-holder-1]
```swift title="Present matching credentials to the holder"
let credential = try await mobileCredentialHolder.getCredential(credentialId: id)
```
* `id` : Pass the identifier of the credential you want to present, as a string. This information is
returned by the
[`createProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/createproximitypresentationsession\(onrequestreceived:onconnected:onsessionterminated:deviceauthenticationoption:blemode:\))
method on a `onRequestReceived` event, triggered when the verifier requests a credential for
verification.
Your application must then gather the user's consent to share the matching credential with the
verifier.
```kotlin title="Present matching credentials to the holder"
val credential = mobileCredentialHolder.getCredential(credentialId)
```
* `credentialId` : Pass the identifier of the credential you want to present, as a string. This
information is returned by the
[`createProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/create-proximity-presentation-session.html?query=suspend%20fun%20createProximityPresentationSession\(activity:%20Activity,%20deviceAuthenticationOption:%20DeviceAuthenticationOption%20=%20DeviceAuthenticationOption.Signature,%20bleMode:%20BleMode%20=%20BleMode.MDocClientCentral,%20onRequestReceived:%20ProximityPresentationSession.OnRequestReceived,%20onConnected:%20ProximityPresentationSession.OnConnected?%20=%20null,%20onSessionTerminated:%20ProximityPresentationSession.OnSessionTerminated?%20=%20null\):%20ProximityPresentationSession)
method on a `onRequestReceived` event, triggered when the verifier requests a credential for
verification.
Your application must then gather the user's consent to share the matching credential with the
verifier.
```ts title="Present matching credentials to the holder"
const credentialResult =
await mobileCredentialHolder.getCredential(credentialId); // [!code highlight]
if (credentialResult.isErr()) {
const { error } = credentialResult;
// handle error scenarios
return;
}
const credential = credentialResult.value;
//
// Send a response to the verifier
//
const sendResponseResult =
await mobileCredentialHolder.sendProximityPresentationResponse({
credentialIds: [credentialId],
terminateSession: false,
});
if (sendResponseResult.isErr()) {
const { error } = sendResponseResult;
// handle error scenarios
return;
}
```
* `credentialId` : Pass the identifier of the credential you want to present, as a string. This
information is returned by the `createProximityPresentationSession` method on a
`onRequestReceived` event, triggered when the verifier requests a credential for verification.
Your application must then gather the user's consent to share the matching credential with the
verifier.
#### Send a response to the verifier [#send-a-response-to-the-verifier-1]
```swift title="Send response to the verifier"
try await proximityPresentationSession.sendResponse(credentialIds: [id])
```
* `id` : Pass the identifier of the credential you want to send, as a string.
```kotlin title="Send response to the verifier"
proximityPresentationSession?.sendResponse(credentialIds = listOf(credentialId))
```
* `credentialId` : Pass the identifier of the credential you want to send, as a string.
```ts title="Send response to the verifier"
const sendResponseResult =
await mobileCredentialHolder.sendProximityPresentationResponse({
credentialIds: [credentialId],
});
if (sendResponseResult.isErr()) {
const { error } = sendResponseResult;
// handle error scenarios
return;
}
```
* `credentialId` : Pass the identifier of the credential you want to send, as a string.
## Next steps [#next-steps]
* Explore the individual tutorials for detailed step-by-step instructions:
* [Credential claiming tutorial](/docs/holding/credential-claiming-tutorial)
* [Remote presentation tutorial](/docs/holding/remote-presentation-tutorial)
* [Proximity presentation tutorial](/docs/holding/proximity-presentation-tutorial)
* Review the [mDocs Holder SDK overview](/docs/holding/sdk-overview) for platform support and capabilities.
* Use the sample app to [claim a credential](/docs/holding/go-hold/getting-started#claim-a-credential) via an Authorization Code flow to experience this flow.
# Trust and security considerations
URL: /docs/holding/trust-and-security
Holding credentials securely means managing which verifiers and issuers your app trusts,
authenticating the holder, and keeping credential status current. This page covers each of those
considerations.
## Trust and security considerations [#trust-and-security-considerations]
### Trusted verifier management [#trusted-verifier-management]
The Holder SDK supports configurable trust lists for verifier certificates. You can adapt your app's
behavior based on trust level:
* Block requests from untrusted verifiers entirely.
* Show warnings or reduced information for unrecognized verifiers.
* For remote presentations, verifier authentication is mandatory. The SDK validates the verifier's
identity before proceeding.
### Trusted issuer management [#trusted-issuer-management]
The SDK maintains a local list of trusted issuer certificates. Only credentials from issuers whose
signing certificates are in your trust list will pass validation. Configure this list based on the
jurisdictions and credential types your app supports.
### Holder authentication [#holder-authentication]
The SDK leverages platform-level security to ensure credentials are only presented by their rightful
holder:
* **Biometric authentication**: require Face ID, Touch ID, or fingerprint before credential access.
* **Device unlock**: ensure the device has a passcode/PIN set.
* **Key binding**: credential keys are bound to the device's secure element or keystore.
* **Per-credential policies**: issuers can define security policies per credential type.
Balance security with inclusion: some users may lack or have disabled biometrics. The SDK supports
OS-level alternatives (PIN, passcode, pattern) as fallbacks.
See the [Device Key Authentication guide](/docs/holding/credential-claiming-guides/device-key-authentication-guide)
for detailed configuration.
### Status list management [#status-list-management]
Credentials can be revoked after issuance. The SDK handles status list retrieval and caching based on
the issuer's configured `ttl` and `exp` parameters:
* **`ttl`**: recommended duration for using a cached status list before fetching an update.
* **`exp`**: hard deadline after which a cached status list must not be used.
Apps can rely on cached statuses between `ttl` and `exp` to maintain offline usability. Consider how
your app communicates credential status changes to the user (e.g., badge indicators, notifications).
See [credential revocation](/docs/issuance/revocation/overview) for background on status lists.
## Next steps [#next-steps]
Next, review the [operational tooling](/docs/holding/operational-tooling) available for your
integration.
# Choose your issuance workflow
URL: /docs/issuance/choosing-a-workflow
The first decision when building an issuance solution is which OID4VCI workflow to use. MATTR VII
supports two, and your choice depends on how much control you need over user authentication during
the claiming process.
## Choose your issuance workflow [#choose-your-issuance-workflow]
MATTR VII supports two OID4VCI workflows. Your choice depends on how much control you need over
user authentication during the claiming process.
### Pre-authorized Code flow [#pre-authorized-code-flow]
Best for: issuing to known users, silent issuance within existing app sessions, high-assurance
credentials where identity has already been verified out-of-band.
The issuer authenticates the user externally, prepares the credential data, and creates a single-use
offer. The wallet claims the credential without an additional authentication step.
* Offers are **single-use**, consumed after successful claim.
* Supports [mDocs](/docs/concepts/mdocs) credential format.
* Optional transaction code adds two-factor security.
* Ideal for in-app issuance or silent credential updates.
**Components involved:**
| Component | Role |
| ---------------------------------------------------------------------------- | ----------------------------------------------- |
| [Credential offer](/docs/issuance/credential-offer/overview) | Contains pre-authorized code and claim data |
| [Credential configuration](/docs/issuance/credential-configuration/overview) | Defines credential structure and claim mappings |
| [Claims source](/docs/issuance/claims-source/overview) (optional) | Enriches credential with additional data |
See the [Pre-authorized Code flow overview](/docs/issuance/pre-authorized-code/overview) for the
detailed workflow and configuration steps.
### Authorization Code flow [#authorization-code-flow]
Best for: public-facing credential offers, user self-service portals, scenarios requiring real-time
identity verification.
The holder scans a QR code or clicks a link, authenticates through your identity provider, and the
credential is issued upon successful authentication.
* Offers are **reusable**: Multiple users can claim from the same offer.
* Supports [mDocs](/docs/concepts/mdocs) and [CWT](/docs/concepts/cwt) credential formats.
* Integrates with your existing OIDC identity provider.
* Supports optional [interaction hooks](/docs/issuance/optional-components#interaction-hooks) for additional verification steps.
**Components involved:**
| Component | Role |
| --------------------------------------------------------------------------------------------- | ----------------------------------------------- |
| [Credential offer](/docs/issuance/credential-offer/overview) | Entry point that starts the flow |
| [Authentication provider](/docs/issuance/authorization-code/authentication-provider/overview) | Authenticates the holder via OIDC |
| [Credential configuration](/docs/issuance/credential-configuration/overview) | Defines credential structure and claim mappings |
| [Claims source](/docs/issuance/claims-source/overview) (optional) | Fetches additional data from external systems |
| [Interaction hook](/docs/issuance/authorization-code/interaction-hook/overview) (optional) | Adds custom steps between auth and issuance |
See the [Authorization Code flow overview](/docs/issuance/authorization-code/overview) for the
detailed workflow and configuration steps.
## Next steps [#next-steps]
Once you have chosen a workflow, [define your credential structure](/docs/issuance/defining-credential-structure).
# Manage credential lifecycle
URL: /docs/issuance/credential-lifecycle
Once issued, credentials may need their status updated and their usage monitored. This page covers
revocation through published status lists and monitoring issuance activity with analytics.
## Manage credential lifecycle [#manage-credential-lifecycle]
Once issued, credentials may need their status updated. MATTR VII supports revocation through
published status lists.
### Revocation [#revocation]
* Configure revocation by enabling `includeStatus` in your credential configuration.
* MATTR VII publishes a [status list](/docs/issuance/revocation/overview#status-list) that verifiers
can retrieve.
* Control caching behavior with two parameters:
* **Time to Live (`ttl`)**: recommended duration for using a retrieved copy of the status list
before fetching the latest version. Defaults to one day.
* **Expiry (`exp`)**: hard deadline after which a retrieved status list must not be used.
Defaults to seven days.
* Wallets and verifiers can rely on cached statuses between `ttl` and `exp` to maintain offline
usability. Setting appropriate values balances up-to-date status checking with user experience.
* Consider how to notify users if their credentials are revoked.
### Monitoring with analytics [#monitoring-with-analytics]
Track issuance activity using MATTR VII's
[Analytics API](/docs/platform-management/analytics/overview):
* Monitor issuance volume and success rates.
* Identify failures (timeout, claim source errors, authentication failures).
* Set up webhooks for real-time event streaming to your monitoring tools.
See the [analytics setup guide](/docs/platform-management/analytics/guide) for configuration.
## Next steps [#next-steps]
Review the [frequently asked questions](/docs/issuance/faq) for answers to common issuance questions.
# How to issue a CWT credential directly
URL: /docs/issuance/cwt-direct-issuance
[CWT credentials](/docs/concepts/cwt) represent claims of data that can be cryptographically proven as
authentic while being compact enough to fit inside a QR code.
This guide will show you how to create and format a new CWT or Semantic CWT credential.
This guide can be used to create both CWT and Semantic CWT credentials. Any
differences in the workflow are highlighted throughout the guide.
## Prerequisites [#prerequisites]
* DIDs:
* Issuer DID: This is a [`did:web`](/docs/concepts/dids) that identifies the issuer who attests the
claims in the credential are accurate.
* You can only sign a CWT credential using DIDs with a `P-256` key type. MATTR VII creates
`did:web`s with this key type (and others) by default.
* [Claim values](/docs/concepts/cwt/data): The claims you wish to include in the credential. This is the
information attested by the issuer.
* The identifier of the desired [PDF](/docs/issuance/cwt-credential-templates/pdf-templates),
[Apple digital pass](/docs/issuance/cwt-credential-templates/apple-templates) or
[Google digital pass](/docs/issuance/cwt-credential-templates/google-templates) template.
## Overview [#overview]
Direct issuance of a CWT credential comprises the following steps:
1. [Sign the CWT credential](#sign-the-cwt-credential).
2. [Format the signed CWT credential](#format-the-signed-cwt-credential).
3. [Share the formatted credential](#share-the-formatted-credential).
### Sign the CWT credential [#sign-the-cwt-credential]
The request to sign the credential is different when you're signing a CWT or Semantic CWT
Credential.
Make a request of the following structure to
[create and sign a new CWT credential](/docs/issuance/direct-issuance-api-reference/cwt-issuance#sign-a-cwt-credential):
```http title="Request"
POST /v2/credentials/compact/sign
```
```json title="Request body"
{
"payload": {
"iss": "did:web:learn.vii.au01.mattr.global",
"nbf": 1704099600,
"exp": 1767258000,
"type": "Course Credential",
"name": "Emma Jane Tasma",
"code": "HS.278",
"certificationName": "Working at Heights",
"certificationLevel": "Level 4",
"issuerName": "Advanced Safety Training",
"expiry": "2026-01-01"
},
"revocable": true,
"isRevoked": false
}
```
* `iss` : Use the Issuer DID. This must be a publicly available and resolvable `did:web` for the
credential to be valid and verifiable.
* `nbf` : Not before. When set, credential verification will fail if the current time is earlier
than the nbf value. Must be expressed as a Unix timestamp.
* `exp` : Expiry. When set, credential verification will fail if the current time is later than
the exp value. Must be expressed as a Unix timestamp.
* `type` : Credential type.
* `name` : Example for a custom claim.
* `code` : Example for a custom claim.
* `certificationName` : Example for a custom claim.
* `certificationLevel` : Example for a custom claim.
* `issuerName` : Example for a custom claim.
* `revocable` : When set to `true`, the signed credential can later be
[revoked](/docs/issuance/revocation/overview). When set to `false`, the credential cannot be
revoked. Defaults to `false`.
* `isRevoked`: When set to `true`, the signed credential is issued as
[revoked](/docs/issuance/revocation/overview), and must be unrevoked to become valid. If
`isRevoked` is provided (e.g. set to either `true` or `false`), `revocable` must be set to
`true`. Defaults to `false`.
*Response*
```json title="Response body"
{
"id": "bKcrxojFSuSZvI5qhKInxA", // [!code highlight]
"decoded": {
"iss": "did:web:learn.vii.au01.mattr.global",
"nbf": 1704099600,
"type": "Course Credential",
"exp": 1767258000,
"name": "Emma Jane Tasma",
"code": "HS.278",
"certificationName": "Working at Heights",
"certificationLevel": "Level 4",
"issuerName": "Advanced Safety Training",
"status": {
// [!code highlight]
"index": 3, // [!code highlight]
"url": "https://learn.vii.au01.mattr.global/v2/credentials/compact/revocation-lists/887cd140-e4d7-4518-b70f-305b23778848" // [!code highlight]
},
"jti": "bKcrxojFSuSZvI5qhKInxA" // [!code highlight]
},
"encoded": "CSC:/1/2KCE3IQEJB5DCMSMGZITM5QBE2QFSALWVQAXQI3ENFSDU53FMI5GYZLBOJXC45TJNEXGC5JQGEXG2YLUORZC4Z3MN5RGC3AFDJSZE7YQHIAACAACOFBW65LSONSSAQ3SMVSGK3TUNFQWYBA2NFLDPEDENZQW2ZLPIVWW2YJAJJQW4ZJAKRQXG3LBMRRW6ZDFMZEFGLRSG44HCY3FOJ2GSZTJMNQXI2LPNZHGC3LFOJLW64TLNFXGOIDBOQQEQZLJM5UHI43SMNSXE5DJMZUWGYLUNFXW4TDFOZSWYZ2MMV3GK3BAGRVGS43TOVSXETTBNVSXQGCBMR3GC3TDMVSCAU3BMZSXI6JAKRZGC2LONFXGOZTFPBYGS4TZNIZDAMRWFUYDCLJQGE5AAAIAACRAEAYDPB2WQ5DUOBZTULZPNRSWC4TOFZ3GS2JOMF2TAMJONVQXI5DSFZTWY33CMFWC6Y3POJSS65RSF5RXEZLEMVXHI2LBNRZS6Y3PNVYGCY3UF5ZGK5TPMNQXI2LPNYWWY2LTORZS6OBYG5RWIMJUGAWWKNDEG4WTINJRHAWWENZQMYWTGMBVMIZDGNZXHA4DIOAH3BAFA3FHFPDIRRKK4SM3ZDTKQSRCPRCYIA7RFUZYQI3RIGDHIGLAODJ6K2F245DTLIIKXAD35TORFQ7MVRJCIEPH6SC6NGA4HRMK76H5V6GXP66FFNX7MNYC6MYVU7ZLLXYVLXBU" // [!code highlight]
}
```
* `id` : Unique credential identifier. This is required when
[revoking a credential](/docs/issuance/revocation/overview).
* `decoded` : This is the decoded version of the credential. It includes all the information
provided in the request, with the addition of the following elements:
* `status` : If `revocable` was set to `true` in the request, this object is used to provide
information required for revocation:
* `index` : This indicates the index of this specific credential within the revocation
list.
* `url` : References the revocation List which holds the revocation status for this
specific credential.
* `jti` : This JWT ID identifies this credential and is identical to the `id` element. When
`revocable` is set to `true`, this value is persisted on the tenant.
* `encoded` : The base32 encoded string representation of the CWT credential. `CSC` stands for
`COSE Sign Compact` profile. You will use this element to convert the credential into a
[format](#format-the-signed-cwt-credential) that can be shared with a holder.
Semantic CWT Credentials represent claims inside a [W3C Verifiable Credential](https://www.w3.org/TR/vc-data-model) data model which is then signed as a CWT credential.
Make a request of the following structure to
[create and sign a new Semantic CWT credential](/docs/issuance/direct-issuance-api-reference/semantic-cwt-issuance#sign-a-semantic-cwt-credential):
```http title="Request"
POST /v2/credentials/compact-semantic/sign
```
```json title="Request body"
{
"payload": {
"iss": "did:web:learn.vii.au01.mattr.global",
"nbf": 1704099600,
"exp": 1767258000,
"vc": {
"type": "Course Credential",
"credentialSubject": {
"name": "Emma Tasma",
"code": "HS.278",
"certificationName": "Working at Heights",
"certificationLevel": "Level 4",
"issuerName": "Advanced Safety Training",
"expiry": "2026-01-01"
}
}
},
"revocable": true,
"isRevoked": false
}
```
* `iss` : Use the Issuer DID. This must be a publicly available and resolvable `did:web` for the
credential to be valid and verifiable.
* `nbf` : Set the Unix epochtime and date the credential will be active from (Unix Epoch time) .
* `exp` : Set the time and date the credential will expire (Unix Epoch time).
* `vc` : This object includes all [custom claims](/docs/concepts/cwt/data) in the credential. These must
comply with the [W3C Verifiable Credentials Data Model](https://www.w3.org/TR/vc-data-model) and
its vocabulary context.
* `revocable` : When set to `true`, the signed credential can later be
[revoked](/docs/issuance/revocation/overview). When set to `false`, the credential cannot be
revoked. Defaults to `false`.
* `isRevoked`: When set to `true`, the signed credential is issued as
[revoked](/docs/issuance/revocation/overview), and must be unrevoked to become valid. If
`isRevoked` is provided (e.g. set to either `true` or `false`), `revocable` must be set to
`true`. Defaults to `false`.
*Response*
```json title="Response body"
{
"id": "urn:uuid:78e19496-8521-424b-8315-35fb1ecaf681", // [!code highlight]
"decoded": {
"iss": "did:web:learn.vii.au01.mattr.global",
"nbf": 1704099600,
"exp": 1767258000,
"vc": {
"type": ["VerifiableCredential", "Course Credential"], // [!code highlight]
"@context": ["https://www.w3.org/2018/credentials/v1"], // [!code highlight]
"credentialSubject": {
"name": "Emma Tasma",
"code": "HS.278",
"certificationName": "Working at Heights",
"certificationLevel": "Level 4",
"issuerName": "Advanced Safety Training",
"expiry": "2026-01-01"
}
},
"status": {
// [!code highlight]
"index": 0, // [!code highlight]
"url": "https://learn.vii.au01.mattr.global/v2/credentials/compact-semantic/revocation-lists/1fe00d6c-904f-497e-bbe1-a3cfdc0b8368" // [!code highlight]
},
"jti": "urn:uuid:78e19496-8521-424b-8315-35fb1ecaf681" // [!code highlight]
},
"encoded": "CSS:/1/2KCE3IQEJB5DCMSMGZITM5QBE2QFSAOZUYAXQI3ENFSDU53FMI5GYZLBOJXC45TJNEXGC5JQGEXG2YLUORZC4Z3MN5RGC3AFDJSZE7YQMJ3GHI3IIBRW63TUMV4HJALYEZUHI5DQOM5C6L3XO53S45ZTFZXXEZZPGIYDCOBPMNZGKZDFNZ2GSYLMOMXXMMLEOR4XAZMCORLGK4TJMZUWCYTMMVBXEZLEMVXHI2LBNRYUG33VOJZWKICDOJSWIZLOORUWC3DRMNZGKZDFNZ2GSYLMKN2WE2TFMN2KMZDOMFWWK2SFNVWWCICUMFZW2YLEMNXWIZLGJBJS4MRXHBYWGZLSORUWM2LDMF2GS33OJZQW2ZLSK5XXE23JNZTSAYLUEBEGK2LHNB2HG4TDMVZHI2LGNFRWC5DJN5XEYZLWMVWGOTDFOZSWYIBUNJUXG43VMVZE4YLNMV4BQQLEOZQW4Y3FMQQFGYLGMV2HSICUOJQWS3TJNZTWMZLYOBUXE6LKGIYDENRNGAYS2MBRAQNGSVRXSA5AAAIAACRAEAADPB7GQ5DUOBZTULZPNRSWC4TOFZ3GS2JOMF2TAMJONVQXI5DSFZTWY33CMFWC6Y3POJSS65RSF5RXEZLEMVXHI2LBNRZS6Y3PNVYGCY3UFVZWK3LBNZ2GSYZPOJSXM33DMF2GS33OFVWGS43UOMXTCZTFGAYGINTDFU4TANDGFU2DSN3FFVRGEZJRFVQTGY3GMRRTAYRYGM3DQB6YIBIHRYMUS2CSCQSLQMKTL6Y6ZL3ICWCAUPGIGEOOWODF77V7ZJPVLGAQC2ZUP7MASXIRTIRRPOIIBKNHKZ4LHROPWBPBCYTKA3GXWIRD736HIJNQENTSFUYIPQ77BG4ZPCTXYIY" // [!code highlight]
}
```
* `id` : Unique credential identifier. This is required when
[revoking a credential](/docs/issuance/revocation/overview).
* `decoded` : This is the decoded version of the credential. It includes all the information
provided in the request, with the addition of the following elements:
* `type` : MATTR VII automatically injects `VerifiableCredential` for all Semantic CWT
credentials.
* `@context` : MATTR VII automatically injects `https://www.w3.org/2018/credentials/v1` for
all Semantic CWT credentials.
* `status` : If `revocable` was set to `true` in the request, this object is used to provide
information required for revocation:
* `index` : This indicates the index of this specific credential within the revocation
list.
* `url` : References the revocation List which holds the revocation status for this
specific credential.
* `jti` : This JWT ID identifies this credential and is identical to the `id` element. When
`revocable` is set to `true`, this value is persisted on the tenant.
* `encoded` : The base32 encoded string representation of the Semantic CWT credential. `CSS`
stands for `COSE Sign Semantic` profile. You will use this element to convert the credential
into a [format](#format-the-signed-cwt-credential) that can be shared with a holder.
### Format the signed CWT credential [#format-the-signed-cwt-credential]
Once the CWT credential is signed, you can render it in one of several formats before sharing it.
Make a request of the following structure to
[format a CWT credential as a QR code](/docs/issuance/direct-issuance-api-reference/cwt-issuance#format-a-cwt-credential-as-a-qr-code):
```http title="Request"
POST /v2/credentials/compact/qrcode
```
You can make a similar request to a different endpoint to
[format a Semantic CWT credential as a QR code](/docs/issuance/direct-issuance-api-reference/semantic-cwt-issuance#format-a-semantic-cwt-credential-as-a-qr-code):
```http title="Request"
POST /v2/credentials/compact-semantic/qrcode
```
```json title="Request body"
{
"payload": "CSC:/1/2KCE3IQEJB5DCMSMGZITM5QBE2QFSALWVQAXQI3ENFSDU53FMI5GYZLBOJXC45TJNEXGC5JQGEXG2YLUORZC4Z3MN5RGC3AFDJSZE7YQHIAACAACOFBW65LSONSSAQ3SMVSGK3TUNFQWYBA2NFLDPEDENZQW2ZLPIVWW2YJAJJQW4ZJAKRQXG3LBMRRW6ZDFMZEFGLRSG44HCY3FOJ2GSZTJMNQXI2LPNZHGC3LFOJLW64TLNFXGOIDBOQQEQZLJM5UHI43SMNSXE5DJMZUWGYLUNFXW4TDFOZSWYZ2MMV3GK3BAGRVGS43TOVSXETTBNVSXQGCBMR3GC3TDMVSCAU3BMZSXI6JAKRZGC2LONFXGOZTFPBYGS4TZNIZDAMRWFUYDCLJQGE5AAAIAACRAEAYDPB2WQ5DUOBZTULZPNRSWC4TOFZ3GS2JOMF2TAMJONVQXI5DSFZTWY33CMFWC6Y3POJSS65RSF5RXEZLEMVXHI2LBNRZS6Y3PNVYGCY3UF5ZGK5TPMNQXI2LPNYWWY2LTORZS6OBYG5RWIMJUGAWWKNDEG4WTINJRHAWWENZQMYWTGMBVMIZDGNZXHA4DIOAH3BAFA3FHFPDIRRKK4SM3ZDTKQSRCPRCYIA7RFUZYQI3RIGDHIGLAODJ6K2F245DTLIIKXAD35TORFQ7MVRJCIEPH6SC6NGA4HRMK76H5V6GXP66FFNX7MNYC6MYVU7ZLLXYVLXBU",
"width": 800
}
```
* `payload` : Use the `encoded` element from the response obtained when
[signing the CWT credential](#sign-the-cwt-credential).
* `width` : Optionally specify the desired width of the output QR code. When no width is specified
MATTR VII will generate a QR code with an optimised width based on the size of the payload.
Maximal value is 1000px.
**Response**
The response includes a QR code as a PNG file. This QR code contains the encoded credential and can
be shared with the intended holder.
**Errors**
If the provided payload is invalid, the API will return one of the following errors:
* `400` :
* Payload is not a string.
* Payload does not match a CWT credential format.
* The credential has expired.
* Unable to validate or decode the payload.
* Generated QR code is larger than the provided width.
* `413` :
* The payload is too large to be stored in a QR code.
Make a request of the following structure to
[format a CWT credential as a PDF](/docs/issuance/direct-issuance-api-reference/cwt-issuance#format-a-cwt-credential-as-a-pdf):
```http title="Request"
POST /v2/credentials/compact/pdf
```
You can make a similar request to a different endpoint to
[format a Semantic CWT credential as a PDF](/docs/issuance/direct-issuance-api-reference/semantic-cwt-issuance#format-a-semantic-cwt-credential-as-a-pdf):
```http title="Request"
POST /v2/credentials/compact-semantic/pdf
```
```json title="Request body"
{
"templateId": "ccad3b98-7086-4556-9e19-9e2aa6ca5c5b",
"payload": "CSC:/1/2KCE3IQEJB5DCMSMGZITM5QBE2QFSALWVQAXQI3ENFSDU53FMI5GYZLBOJXC45TJNEXGC5JQGEXG2YLUORZC4Z3MN5RGC3AFDJSZE7YQHIAACAACOFBW65LSONSSAQ3SMVSGK3TUNFQWYBA2NFLDPEDENZQW2ZLPIVWW2YJAJJQW4ZJAKRQXG3LBMRRW6ZDFMZEFGLRSG44HCY3FOJ2GSZTJMNQXI2LPNZHGC3LFOJLW64TLNFXGOIDBOQQEQZLJM5UHI43SMNSXE5DJMZUWGYLUNFXW4TDFOZSWYZ2MMV3GK3BAGRVGS43TOVSXETTBNVSXQGCBMR3GC3TDMVSCAU3BMZSXI6JAKRZGC2LONFXGOZTFPBYGS4TZNIZDAMRWFUYDCLJQGE5AAAIAACRAEAYDPB2WQ5DUOBZTULZPNRSWC4TOFZ3GS2JOMF2TAMJONVQXI5DSFZTWY33CMFWC6Y3POJSS65RSF5RXEZLEMVXHI2LBNRZS6Y3PNVYGCY3UF5ZGK5TPMNQXI2LPNYWWY2LTORZS6OBYG5RWIMJUGAWWKNDEG4WTINJRHAWWENZQMYWTGMBVMIZDGNZXHA4DIOAH3BAFA3FHFPDIRRKK4SM3ZDTKQSRCPRCYIA7RFUZYQI3RIGDHIGLAODJ6K2F245DTLIIKXAD35TORFQ7MVRJCIEPH6SC6NGA4HRMK76H5V6GXP66FFNX7MNYC6MYVU7ZLLXYVLXBU"
}
```
* `templateId` : Use the `id` of the [PDF template](/docs/issuance/cwt-credential-templates/pdf-templates) you
wish to use to format this credential.
* `payload` : Use the `encoded` element from the response obtained when
[signing the CWT credential](#sign-the-cwt-credential).
**Response**
The response includes a PDF document with the information from the credential formatted according to
the PDF template.
**Errors**
Bad request (400) Errors may occur in the following scenarios:
* Payload is not a string.
* Payload does not match a CWT credential format.
* The credential has expired.
* Unable to validate or decode the payload.
* The PDF template does not exist.
* The decoded payload doesn't include a claim required for the PDF template.
Your Apple developer certificates must be valid when attempting to format CWT credentials as
Apple digital passes.
Make a request of the following structure to
[format a CWT credential as an Apple digital pass](/docs/issuance/direct-issuance-api-reference/cwt-issuance#format-a-cwt-credential-as-an-apple-pass):
```http title="Request"
POST /v2/credentials/compact/digital-pass/apple
```
You can make a similar request to a different endpoint to
[format a Semantic CWT credential as an Apple digital pass](/docs/issuance/direct-issuance-api-reference/semantic-cwt-issuance#format-a-semantic-cwt-credential-as-an-apple-pass):
```http title="Request"
POST /v2/credentials/compact-semantic/digital-pass/apple
```
```json title="Request body"
{
"templateId": "1b04f0ee-8e3e-4153-a0e0-8603a10e7f0a",
"payload": "CSC:/1/2KCE3IQEJB5DCMSMGZITM5QBE2QFSALWVQAXQI3ENFSDU53FMI5GYZLBOJXC45TJNEXGC5JQGEXG2YLUORZC4Z3MN5RGC3AFDJSZE7YQHIAACAACOFBW65LSONSSAQ3SMVSGK3TUNFQWYBA2NFLDPEDENZQW2ZLPIVWW2YJAJJQW4ZJAKRQXG3LBMRRW6ZDFMZEFGLRSG44HCY3FOJ2GSZTJMNQXI2LPNZHGC3LFOJLW64TLNFXGOIDBOQQEQZLJM5UHI43SMNSXE5DJMZUWGYLUNFXW4TDFOZSWYZ2MMV3GK3BAGRVGS43TOVSXETTBNVSXQGCBMR3GC3TDMVSCAU3BMZSXI6JAKRZGC2LONFXGOZTFPBYGS4TZNIZDAMRWFUYDCLJQGE5AAAIAACRAEAYDPB2WQ5DUOBZTULZPNRSWC4TOFZ3GS2JOMF2TAMJONVQXI5DSFZTWY33CMFWC6Y3POJSS65RSF5RXEZLEMVXHI2LBNRZS6Y3PNVYGCY3UF5ZGK5TPMNQXI2LPNYWWY2LTORZS6OBYG5RWIMJUGAWWKNDEG4WTINJRHAWWENZQMYWTGMBVMIZDGNZXHA4DIOAH3BAFA3FHFPDIRRKK4SM3ZDTKQSRCPRCYIA7RFUZYQI3RIGDHIGLAODJ6K2F245DTLIIKXAD35TORFQ7MVRJCIEPH6SC6NGA4HRMK76H5V6GXP66FFNX7MNYC6MYVU7ZLLXYVLXBU"
}
```
* `templateId` : Use the `id` of the
[Apple digital pass template](/docs/issuance/cwt-credential-templates/apple-templates) you wish to use to
format this credential.
* `payload` : Use the `encoded` element from the response obtained when
[signing the CWT credential](#sign-the-cwt-credential).
**Response**
The response includes a signed bundle `.pkpass` file (binary format).
**Errors**
Bad request (400) Errors may occur in the following scenarios:
* Payload is not a string.
* Payload does not match a CWT credential format.
* The credential has expired.
* Unable to validate or decode the payload.
* Apple developer account credentials are missing or incorrect.
* The template does not exist or is incorrectly defined.
* Required `config.json` fields are not defined.
* The file name contains a special character that is not supported by the Apple pass template
endpoint.
* The decoded payload is missing a required value for `pass.json`.
Your Google developer certificates must be valid when attempting to format CWT credentials as
Google digital passes.
Make a request of the following structure to
[format a CWT credential as a Google digital pass](/docs/issuance/direct-issuance-api-reference/cwt-issuance#format-a-cwt-credential-as-a-google-pass):
```http title="Request"
POST /v2/credentials/compact/digital-pass/google
```
You can make a similar request to a different endpoint to
[format a Semantic CWT credential as a Google digital pass](/docs/issuance/direct-issuance-api-reference/semantic-cwt-issuance#format-a-semantic-cwt-credential-as-a-google-pass):
```http title="Request"
POST /v2/credentials/compact-semantic/digital-pass/google
```
```json title="Request body"
{
"templateId": "1b04f0ee-8e3e-4153-a0e0-8603a10e7f0a",
"payload": "CSC:/1/2KCE3IQEJB5DCMSMGZITM5QBE2QFSALWVQAXQI3ENFSDU53FMI5GYZLBOJXC45TJNEXGC5JQGEXG2YLUORZC4Z3MN5RGC3AFDJSZE7YQHIAACAACOFBW65LSONSSAQ3SMVSGK3TUNFQWYBA2NFLDPEDENZQW2ZLPIVWW2YJAJJQW4ZJAKRQXG3LBMRRW6ZDFMZEFGLRSG44HCY3FOJ2GSZTJMNQXI2LPNZHGC3LFOJLW64TLNFXGOIDBOQQEQZLJM5UHI43SMNSXE5DJMZUWGYLUNFXW4TDFOZSWYZ2MMV3GK3BAGRVGS43TOVSXETTBNVSXQGCBMR3GC3TDMVSCAU3BMZSXI6JAKRZGC2LONFXGOZTFPBYGS4TZNIZDAMRWFUYDCLJQGE5AAAIAACRAEAYDPB2WQ5DUOBZTULZPNRSWC4TOFZ3GS2JOMF2TAMJONVQXI5DSFZTWY33CMFWC6Y3POJSS65RSF5RXEZLEMVXHI2LBNRZS6Y3PNVYGCY3UF5ZGK5TPMNQXI2LPNYWWY2LTORZS6OBYG5RWIMJUGAWWKNDEG4WTINJRHAWWENZQMYWTGMBVMIZDGNZXHA4DIOAH3BAFA3FHFPDIRRKK4SM3ZDTKQSRCPRCYIA7RFUZYQI3RIGDHIGLAODJ6K2F245DTLIIKXAD35TORFQ7MVRJCIEPH6SC6NGA4HRMK76H5V6GXP66FFNX7MNYC6MYVU7ZLLXYVLXBU"
}
```
* `templateId` : Use the `id` of the
[Google digital pass template](/docs/issuance/cwt-credential-templates/google-templates) you wish to use to
format this credential.
* `payload` : Use the `encoded` element from the response obtained when
[signing the CWT credential](#sign-the-cwt-credential).
*Response*
```json title="Response body"
{
"redirectTo": "https://pay.google.com/gp/v/save/ejqEeFUtiFRF2t_0hXelmd1TDaeoPES091NT7LBiDvrmKpYPrOlhBfeSKOaA"
}
```
* `redirectTo` : This is the URL where the digital pass is available to download.
**Errors**
Bad request (400) Errors may occur in the following scenarios:
* Payload is not a string.
* Payload does not match a CWT credential format.
* The credential has expired.
* Unable to validate or decode the payload.
* Google account credentials are missing or incorrect
* The template does not exist or is incorrectly defined.
* The decoded payload doesn't include a field required in `template.json`.
### Share the formatted credential [#share-the-formatted-credential]
Now that you have the credential rendered in the required format, you can share it with its intended
holder.
Share the QR code image using your preferred communication channel (e.g. e-mail, messaging platform, etc.).
Share the PDF document using your preferred communication channel (e.g. e-mail, messaging platform, etc.).
Share the `.pkpass` file using your preferred communication channel (e.g. e-mail, messaging
platform, etc.). The recipient will be able to save the file into their Apple wallet.
You can use the "Add to Apple Wallet" badge as a visual cue. Download files
from [Add to Apple Wallet badge
guidelines](https://developer.apple.com/wallet/add-to-apple-wallet-guidelines/).
Share the download URL using your preferred communication channel (e.g. e-mail, messaging platform,
etc.). The recipient can use the link to download the credential and save it into their Google
wallet.
You can use the "Add to Google Wallet" badge as a visual cue. Download files
from [Google Wallet brand
guidelines](https://developers.google.com/wallet/generic/resources/brand-guidelines).
# Define your credential structure
URL: /docs/issuance/defining-credential-structure
Before issuing, you need to define what data the credential will contain and how it maps from your
source systems. This page covers the credential configuration template, how to map your data, and
where claim data can come from.
## Define your credential structure [#define-your-credential-structure]
Before issuing, you need to define what data the credential will contain and how it maps from your
source systems.
### Credential configuration [#credential-configuration]
A [credential configuration](/docs/issuance/credential-configuration/overview) is the template that
defines:
* **Credential type and format**: mDocs (ISO/IEC 18013-5) or CWT.
* **Claim mappings**: How source data maps to credential attributes, organized by namespace.
* **Validity rules**: `validFrom`, `validUntil`, or relative expiry (`expiresIn`).
* **Revocation**: Whether to include status list references.
* **Branding**: Display metadata for wallet rendering (name, logo, colors).
### Credential data mapping [#credential-data-mapping]
Map your existing data models (e.g., from legacy digital credentials or other data sources) to the
relevant ISO/IEC 18013-5 (or equivalent) schema. All claim mapping logic, including translation of
custom or legacy fields, should occur in your [claims source](/docs/issuance/claims-source/overview)
or be handled before passing data to the credential configuration.
### Claim data sources [#claim-data-sources]
Claims can come from multiple sources depending on your workflow:
| Source | Available in | Description |
| ------------------------------------------------------ | ------------------- | ------------------------------------------------ |
| Authentication provider (ID token) | Authorization Code | Claims from the user's OIDC authentication |
| Interaction hook response | Authorization Code | Claims returned by your custom interaction step |
| Credential offer payload | Pre-authorized Code | Claims supplied directly when creating the offer |
| [Claims source](/docs/issuance/claims-source/overview) | Both flows | External API call to fetch additional data |
## Next steps [#next-steps]
With your credential structure defined, [deliver credential offers to holders](/docs/issuance/delivering-offers).
# Deliver credential offers to holders
URL: /docs/issuance/delivering-offers
The credential offer is what starts the issuance flow for the holder. This page covers how offers
reach your users and which wallet applications can claim them.
## Deliver credential offers to holders [#deliver-credential-offers-to-holders]
The credential offer is what starts the issuance flow for the holder. You need to decide how offers
reach your users and which wallet applications can claim them.
### Offer delivery methods [#offer-delivery-methods]
| Method | Best for | Description |
| ----------- | ----------------------------------- | -------------------------------------------- |
| QR code | In-person kiosks, printed materials | User scans with their wallet app |
| Deep link | Email, SMS, in-app notifications | Clickable URL that opens the wallet |
| Silent push | Existing app sessions | Credential delivered without user initiation |
### Wallet targeting [#wallet-targeting]
You can control which wallet apps can claim your offers:
* **Open model**: any OID4VCI-compatible wallet can claim using the standard
`openid-credential-offer://` scheme.
* **Private-use scheme**: a custom URI scheme targets specific wallet applications.
* **Claimed HTTPS links**: App Links (Android) or Universal Links (iOS) provide the strongest
app-binding with domain verification.
* **[Wallet attestation](/docs/issuance/credential-issuance/wallet-attestation)**: requires wallets
to cryptographically prove their authenticity before claiming credentials. This provides the
strongest level of wallet trust by validating the wallet instance through a certificate-based
trust chain, ensuring only authorized wallet applications can receive your credentials.
See [claiming credential offers](/docs/issuance/credential-offer/overview#claiming-credential-offers)
for detailed configuration guidance.
## Next steps [#next-steps]
Extend the basic workflow with [optional components](/docs/issuance/optional-components) such as an
authentication provider, claims source, or interaction hook.
# Direct Credential issuance
URL: /docs/issuance/direct-issuance-overview
Direct issuance begins with making an API request to a MATTR VII endpoint, providing a credential
payload. The endpoint responds with a cryptographically signed digital credential. MATTR VII
currently supports direct issuance of [CWT credentials](/docs/concepts/cwt) and [Semantic CWT credentials](/docs/concepts/cwt).
Once the credential is cryptographically signed, it can be shared with its intended holder via
various communication channels, depending on the credential format and your implementation.
**CWT and Semantic CWT credentials** can be [formatted](/docs/issuance/cwt-direct-issuance#format-the-signed-cwt-credential) as a QR code, PDF
document, Apple or Google digital passes. Formatting is based on templates you create and upload
to your MATTR VII tenant. Formatted credentials can be shared with holders via e-mail, messaging
systems or even as paper-based credentials (for QR codes and PDFs).
# Frequently asked questions
URL: /docs/issuance/faq
Answers to common questions about credential issuance with MATTR VII. For the concepts behind these
answers, see the [issuance overview](/docs/issuance).
## Frequently asked questions [#frequently-asked-questions]
### What credential formats does MATTR VII support for issuance? [#what-credential-formats-does-mattr-vii-support-for-issuance]
MATTR VII supports [mDocs](/docs/concepts/mdocs) (aligned with ISO/IEC 18013-5) and
[CWT/Semantic CWT](/docs/concepts/cwt) credential formats through OID4VCI. Direct issuance
(API-based, no wallet interaction) is available for CWT credentials.
### Which wallets can receive credentials issued by MATTR VII? [#which-wallets-can-receive-credentials-issued-by-mattr-vii]
Any wallet that implements the
[OID4VCI specification](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html)
can receive credentials. You can target specific wallets using custom URI schemes or claimed HTTPS
links, or allow any compatible wallet using the standard scheme.
MATTR also provides its own holding solutions that are fully compatible with MATTR VII issuance:
* **[MATTR Holder SDKs](/docs/holding)**: embed credential holding capabilities directly into your
own mobile application (iOS, Android, React Native).
* **[MATTR GO](/docs/holding/go-hold/getting-started)**: a ready-to-use wallet application for end
users.
### Do I need my own identity provider? [#do-i-need-my-own-identity-provider]
For the Authorization Code flow, yes, you need an OIDC-compliant identity provider. For the
Pre-authorized Code flow, no. You authenticate users through your own means before creating the
credential offer.
### Can I issue credentials without user interaction? [#can-i-issue-credentials-without-user-interaction]
Yes. The Pre-authorized Code flow supports silent issuance within existing app sessions. Your
application creates the credential offer programmatically and the wallet claims it without requiring
the user to authenticate again.
### How do I populate credential data from my existing systems? [#how-do-i-populate-credential-data-from-my-existing-systems]
Use a [claims source](/docs/issuance/claims-source/overview), an HTTPS endpoint that MATTR VII calls
to fetch data from your backend systems. Claims are mapped into the credential via your credential
configuration's claim mappings.
### What happens if my claims source is unavailable? [#what-happens-if-my-claims-source-is-unavailable]
If the claims source times out (>3 seconds) or returns a non-2xx response, issuance fails. Design
your claims source for high availability and consider which claims are marked as `required` vs
optional with `defaultValue` fallbacks.
### Can I add custom verification steps before issuing? [#can-i-add-custom-verification-steps-before-issuing]
Yes. Use an [interaction hook](/docs/issuance/authorization-code/interaction-hook/overview) in the
Authorization Code flow to add biometric checks, consent flows, or other custom logic between
authentication and issuance.
### How do I revoke a credential after issuance? [#how-do-i-revoke-a-credential-after-issuance]
Use the MATTR VII revocation API to update the credential's status. The updated status is reflected
in the published status list that verifiers check. See
[credential revocation](/docs/issuance/revocation/overview) for details.
### Can I issue multiple credentials in one flow? [#can-i-issue-multiple-credentials-in-one-flow]
Yes. A credential offer can reference multiple credential configurations, allowing the holder to
claim several credentials in a single interaction.
### What is the maximum validity period for an mDL? [#what-is-the-maximum-validity-period-for-an-mdl]
mDL credentials have a maximum validity constraint of 427 days, enforced by the credential
configuration. Plan your refresh or re-issuance strategy accordingly.
# Credential Issuance
URL: /docs/issuance
Issuing verifiable credentials (such as mobile driver's licenses (mDLs), employee badges, health
cards, or proof-of-age tokens) requires orchestrating identity verification, data sourcing,
cryptographic signing, and secure delivery to a holder's wallet. As an implementer, you need to
understand the components involved and how they fit together within your existing systems.
This overview walks you through the key architectural decisions for building a credential issuance
solution using [MATTR VII](/docs/digital-trust-service), from selecting your issuance workflow through
to managing credential lifecycle. Start here, then follow the pages in order or jump to the topic you
need.
## How credential issuance works [#how-credential-issuance-works]
At its core, credential issuance involves three parties:
1. **Issuer**: Your organization, using MATTR VII to create and sign credentials.
2. **Holder**: The end user who receives and stores the credential in their wallet.
3. **Wallet**: An application that claims, stores, and later presents the credential.
The issuance process follows the
[OpenID for Verifiable Credential Issuance (OID4VCI)](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html)
standard. Your application creates a credential offer, the holder's wallet resolves issuer metadata,
authenticates (if required), and receives a cryptographically signed credential.
The result is a credential that is:
* **Tamper-evident**: Any modification invalidates the signature.
* **Verifiable**: Any relying party can confirm authenticity against the issuer's certificate chain without contacting the issuer.
* **Portable**: Stored in the holder's wallet and presentable across contexts.
* **Revocable**: Status can be updated after issuance.
## Underlying platforms [#underlying-platforms]
All issuance capabilities are provided through MATTR VII tenants, accessible via the MATTR Portal or API.
## Explore the overview [#explore-the-overview]
Work through these pages to design your issuance solution from simple to more complex concepts:
1. [Choose your issuance workflow](/docs/issuance/choosing-a-workflow): Pre-authorized Code versus Authorization Code.
2. [Define your credential structure](/docs/issuance/defining-credential-structure): credential configuration, data mapping, and claim data sources.
3. [Deliver credential offers to holders](/docs/issuance/delivering-offers): delivery methods and wallet targeting.
4. [Optional components](/docs/issuance/optional-components): authentication provider, claims source, and interaction hooks.
5. [Manage issuer keys and certificates](/docs/issuance/keys-and-certificates): key management options and the certificate chain of trust.
6. [Manage credential lifecycle](/docs/issuance/credential-lifecycle): revocation and monitoring with analytics.
7. [Frequently asked questions](/docs/issuance/faq): answers to common issuance questions.
# Direct issuance journey pattern
URL: /docs/issuance/issuance-journey-pattern-direct
In this method you make a direct API request to sign a provided payload as a digital credential, and
then share it with its intended holder.
* **Issuance channel**: Remote, Unsupervised
* **Device/s**: Cross-device
* **Formats**: CWT
* **Information assurance level**: High
* **Identity assurance level**: Low or High (with accompanying identification)
## Journey flow [#journey-flow]
### Receiving notification a credential is available [#receiving-notification-a-credential-is-available]
Samantha receives an email confirming the issuance of a credential that she can claim.
### Unique access code [#unique-access-code]
The email includes instructions along with a unique code that Samantha can use to claim a digital
version of the credential into her digital wallet or device.
### Providing identifying information [#providing-identifying-information]
Samantha navigates to the provided website and enters the digital access code printed in the email,
along with a piece of information that isn’t provided in the instructions, that only she would know
* like her date of birth.
### Claiming the credential [#claiming-the-credential]
Samantha is able to view the credential through the website and then select to save it as a:
* QR code.
* PDF document.
* Digital pass in her Google Pay pass, Apple Wallet pass or any 3rd party wallet.
### Validating credential issuer [#validating-credential-issuer]
When Samantha is prompted to accept the credential, her wallet is checking that this Issuer is
allowed to issue this type of credential in her current trust network.
This is an optional feature for network operators to increased the level of trust.
### Viewing the credential in wallet [#viewing-the-credential-in-wallet]
After accepting the credential, it is available in Samantha’s digital wallet.
## Architecture [#architecture]
### Logging into the portal [#logging-into-the-portal]
The Issuer’s portal (1) is the starting point. The portal could be accessed either directly by the
user or some other form of digital journey that brings them to this step. Once authenticated in the
portal, which could simply be through the entering of a link or piece of information, the Issuer can
attempt to match the user to a record in the data store (2). The portal can then prompt the user to
claim a credential.
### Credential generation [#credential-generation]
The credential Issuer then retrieves and bundles the information relating to the requesting user
from their own data source (2). The data is passed to the Credential Generation component (3) which
formats and signs the data into a credential, ready to be shared with the user as a QR code, PDF or
a digital pass. This step could include storing on a device/form of storage that doesn’t require the
credential to be digitally bound to the user.
### Bearer credentials [#bearer-credentials]
The only verifiable binding that can occur on this implementation pattern is between the credential
and the Issuer, as there are no authentication or device binding attributes supplied to build into
the credential.
Binding the user to the information can only occur by comparing the claims in the credential to
another trusted information source (for example, comparing the details in the credential with
another verifiable credential bound to the user’s device).
# Manage issuer keys and certificates
URL: /docs/issuance/keys-and-certificates
Credentials must be signed with keys your organization controls. This page covers your key management
options and the certificate chain of trust that lets verifiers recognize your credentials.
## Manage issuer keys and certificates [#manage-issuer-keys-and-certificates]
Credentials must be signed with keys your organization controls. The signing certificate chain
establishes trust. Verifiers check that the credential was signed by a recognized issuer.
### Key management options [#key-management-options]
| Option | Description | Use case |
| ------------------------------------------------------------------- | ------------------------------------------- | ------------------------------- |
| MATTR managed KMS (SSM) | Software-based key storage managed by MATTR | Default for most deployments |
| MATTR managed KMS (HSM) | Hardware Security Module for key protection | High-assurance requirements |
| [External KMS](/docs/concepts/chain-of-trust#external-certificates) | Bring your own key management solution | Organizations with existing PKI |
Refer to the [PKI spec sheet](https://files.mattr.global/specsheets/mcc-pki.pdf) for detailed
information on MATTR's key management capabilities.
### Certificate chain of trust [#certificate-chain-of-trust]
For mDL issuance, your signing certificates must chain to an
[Issuing Authority Certificate Authority (IACA)](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca)
root. This is the same root that verifiers check during presentation.
See the [certificates overview](/docs/issuance/certificates/overview) and
[chain of trust concepts](/docs/concepts/chain-of-trust) for detailed guidance.
## Next steps [#next-steps]
Finally, learn how to [manage credential lifecycle](/docs/issuance/credential-lifecycle) after
issuance.
# OID4VCI
URL: /docs/issuance/oid4vci-overview
Description: A plain-language guide to OID4VCI (OpenID for Verifiable Credential Issuance) - what it is, how it works, how to connect a system of record to a credential issuance flow, and how MATTR VII and the MATTR Portal support it.
## What is OID4VCI? [#what-is-oid4vci]
**OID4VCI** stands for **OpenID for Verifiable Credential Issuance**. It is an open standard
published by the OpenID Foundation that defines how a credential issuer (such as a
government department, financial institution, or employer) can deliver verifiable
credentials to a digital wallet in a secure and interoperable way.
OID4VCI builds on **OAuth 2.0** and **OpenID Connect**, two protocols already widely used
across the web for authentication and authorization. Reusing those foundations means
issuers, wallets, and identity providers can adopt OID4VCI without reinventing core
security plumbing.
The full specification is available at the
[OpenID Foundation](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html).
If you are unfamiliar with OpenID Connect, the identity protocol underpinning
the OpenID provisioning capability, there are many excellent guides available
online such as this guide from
[Google](https://developers.google.com/identity/openid-connect/openid-connect),
or this guide from
[Mozilla](https://infosec.mozilla.org/guidelines/iam/openid_connect.html).
## Why OID4VCI matters [#why-oid4vci-matters]
Before OID4VCI, an issuer wanting to deliver a digital credential to a wallet often had to
build a bespoke integration with each wallet. That model does not scale. Holders end up
with a fragmented experience, issuers carry the cost of multiple integrations, and the
ecosystem grows slowly because every new participant has to coordinate with every other.
OID4VCI changes the model. Any wallet that implements the standard can receive credentials
from any issuer that implements it, regardless of vendor. That unlocks:
* **Interoperability**: A single issuance integration that works across many wallets.
* **Choice for holders**: Holders pick the wallet that suits them, and any compliant wallet
can hold the credential.
* **Lower integration cost**: Issuers do not need to maintain a separate code path per
wallet provider.
* **Standards alignment**: OID4VCI aligns with regulatory and ecosystem direction, such as
the European Digital Identity Wallet (EUDI) framework.
## How OID4VCI works at a high level [#how-oid4vci-works-at-a-high-level]
At a glance, OID4VCI follows three steps:
1. **Offer**: The issuer prepares an offer, which tells the wallet what credentials are
available and how to claim them.
2. **Authorize**: The wallet (and where relevant, the holder) authorizes the credential
request. The exact mechanism depends on which OID4VCI flow is used.
3. **Issue**: The wallet receives an access token, requests the credential, and the issuer
responds with a signed verifiable credential.
The specification accommodates different real-world scenarios through two distinct flows.
## OID4VCI flows [#oid4vci-flows]
OID4VCI defines two distinct workflows, each tailored to different use cases and
requirements:
* [Authorization Code flow](/docs/issuance/authorization-code/overview): This interactive,
user-driven flow requires the credential recipient (typically a wallet) to redirect the
user to the issuer (such as a government or organization) for authentication. After the
user successfully authenticates and gives consent, the issuer's authentication provider
returns an authorization code. The wallet then exchanges this code for an access token,
which is used to obtain the credential.
The following credential formats can be issued via the Authorization Code flow:
* [CWT](/docs/concepts/cwt)
* [Semantic CWT](/docs/concepts/cwt)
* [mDocs](/docs/concepts/mdocs)
* [Pre-authorized Code flow](/docs/issuance/pre-authorized-code/overview): In this flow,
the issuer prepares the credential issuance in advance and may authenticate and
authorize the holder ahead of time. Instead of obtaining an authorization code through
user authentication, the wallet receives a pre-authorized code directly from the issuer,
often via an out-of-band method. The user does not need to authenticate again and the
wallet presents the pre-authorized code to retrieve an access token and then claim the
credential. For added security, the issuer can require a transaction code (shared
separately with the holder) which the wallet must also provide to claim the credential.
The Pre-authorized Code flow is only supported for [mDocs](/docs/concepts/mdocs).
MATTR VII supports both workflows, allowing you to choose the one that best fits your use
case.
## Connecting a system of record to an issuance flow [#connecting-a-system-of-record-to-an-issuance-flow]
Most organizations already hold the data they want to issue as a credential in an existing
system: a customer database, an HR system, a license registry, a learning record store.
OID4VCI does not replace that system. Instead, MATTR VII connects to it at the moment of
issuance and uses the existing data to populate the credential.
In MATTR VII, this connection is modeled as a **claims source**. A claims source defines:
* Where the source data lives (an HTTP endpoint, for example).
* How MATTR VII authenticates to that endpoint.
* How the response is mapped onto the claims of the credential being issued.
A typical end-to-end issuance setup looks like this:
1. **Define the credential**: Create a credential configuration that describes the
credential format, the claims it carries, and how it is signed.
2. **Connect the system of record**: Configure a [claims source](/docs/issuance/claims-source/overview)
so MATTR VII knows where to fetch the holder's data at issuance time.
3. **Choose an issuance flow**: Decide between the
[Authorization Code flow](/docs/issuance/authorization-code/overview) and the
[Pre-authorized Code flow](/docs/issuance/pre-authorized-code/overview), based on
whether the holder needs to authenticate interactively.
4. **Deliver the offer**: Generate a credential offer and deliver it to the holder, for
example via a QR code, deep link, or in-app prompt.
5. **Issue the credential**: When the holder accepts the offer in their wallet, MATTR VII
retrieves the relevant data from the claims source, assembles and signs the credential,
and delivers it through OID4VCI.
Once issued, the credential lives in the holder's wallet and can be presented to verifiers
without going back to the issuer.
## How to get started with OID4VCI on MATTR [#how-to-get-started-with-oid4vci-on-mattr]
You can build and operate an OID4VCI issuance flow with MATTR through two complementary
surfaces:
* **[MATTR Portal](https://portal.mattr.global)**: A web interface for managing your MATTR
VII tenants. The Portal lets you create credential configurations, manage claims
sources, configure issuance flows, and monitor activity without writing code. It is the
fastest way to stand up an end-to-end issuance flow for prototyping or production use.
Learn more in the [MATTR Portal documentation](/docs/platform-management/portal).
* **[MATTR VII API](/docs/api-reference)**: A REST API for full programmatic control over
every aspect of issuance, including credential configurations, claims sources, offers,
and webhook events. Use the API when you need to embed issuance into your own systems or
automate large-scale operations.
For a guided walkthrough, see the
[Credential Issuance overview](/docs/issuance).
If you are still deciding which approach fits your use case, or would like a deeper
conversation about your issuance design, [contact us](mailto:dev-support@mattr.global).
## Frequently asked questions [#frequently-asked-questions]
### What is OID4VCI? [#what-is-oid4vci-1]
**OID4VCI (OpenID for Verifiable Credential Issuance)** is an open standard from the
OpenID Foundation that defines how a digital credential issuer can deliver verifiable
credentials to a digital wallet in a secure and interoperable way. It builds on **OAuth
2.0** and **OpenID Connect**, two protocols already widely used for authentication and
authorization on the web.
### Why does OID4VCI matter for credential issuance? [#why-does-oid4vci-matter-for-credential-issuance]
OID4VCI gives issuers a standards-based, interoperable way to deliver verifiable
credentials to any compliant wallet, instead of building a bespoke integration per wallet.
That means issuers can reach a broader holder base, wallets can support credentials from
many issuers, and ecosystems can grow without each participant having to coordinate
one-to-one.
### What are the two OID4VCI flows? [#what-are-the-two-oid4vci-flows]
OID4VCI defines two flows:
* **Authorization Code flow**: The holder is redirected to the issuer to authenticate and
consent. The wallet then exchanges an authorization code for an access token and
retrieves the credential.
* **Pre-authorized Code flow**: The issuer prepares the credential in advance and hands
the wallet a pre-authorized code (often via a QR code or link), so the holder does not
need to authenticate again at issuance time. A transaction code can be added for
additional security.
### How do I connect a system of record to an OID4VCI issuance flow? [#how-do-i-connect-a-system-of-record-to-an-oid4vci-issuance-flow]
A system of record (the database or application that already holds the user's data) is
connected to MATTR VII through a **claims source**. The claims source tells MATTR VII how
to retrieve the data it needs to populate the credential at the moment of issuance. From
the holder's perspective, they trigger the flow, authenticate (if required), and receive
the credential into their wallet. Behind the scenes, MATTR VII calls the claims source,
assembles the credential, signs it, and delivers it through OID4VCI.
### How do MATTR VII and the MATTR Portal support OID4VCI? [#how-do-mattr-vii-and-the-mattr-portal-support-oid4vci]
MATTR VII supports both OID4VCI flows (Authorization Code and Pre-authorized Code) and
multiple credential formats. You can configure issuance through the **MATTR VII Platform
API**, or through the **MATTR Portal**, which provides a user interface for managing
tenants, credential configurations, claims sources, and issuance flows without writing
code.
### Which credential formats can MATTR VII issue via OID4VCI? [#which-credential-formats-can-mattr-vii-issue-via-oid4vci]
Via the **Authorization Code flow**, MATTR VII can issue **CWT**, **Semantic CWT**, and
**mDoc** credentials. Via the **Pre-authorized Code flow**, MATTR VII issues **mDoc**
credentials.
### Is OID4VCI the same as OpenID Connect? [#is-oid4vci-the-same-as-openid-connect]
No. **OpenID Connect** is a protocol for identity authentication, typically used to sign a
user into an application. **OID4VCI** builds on the same underlying OAuth 2.0 foundations
as OpenID Connect, but it is purpose-built for issuing verifiable credentials, not for
authenticating sessions. The two are complementary and often used together (for example,
OpenID Connect can authenticate the holder during the Authorization Code flow).
## Summary [#summary]
OID4VCI is the open standard that lets credential issuers deliver verifiable credentials
to wallets in a way that works across the ecosystem. It defines two flows (Authorization
Code and Pre-authorized Code) to suit different use cases, and connects to your existing
systems of record through a claims source. MATTR VII supports OID4VCI out of the box, with
both the [MATTR Portal](https://portal.mattr.global) and the
[MATTR VII API](/docs/api-reference) available to configure and operate your issuance
flows.
# Optional components
URL: /docs/issuance/optional-components
These components extend the basic issuance workflow with additional capabilities. Add them only when
your assurance or data requirements call for them.
## Optional components [#optional-components]
These components extend the basic issuance workflow with additional capabilities.
### Authentication provider [#authentication-provider]
An [authentication provider](/docs/issuance/authorization-code/authentication-provider/overview) is
your existing OIDC-compliant identity provider (e.g., Auth0, Entra ID, Okta). It authenticates the
holder during the Authorization Code flow.
**Requirements:**
* Must support OIDC Discovery (`.well-known/openid-configuration`).
* Must support the authorization code grant.
* Must support the `state` parameter.
* One authentication provider per MATTR VII tenant.
**How it fits in the workflow:**
1. Holder initiates credential claim.
2. MATTR VII redirects to your identity provider.
3. Holder authenticates and consents.
4. ID token claims become available for credential mapping.
Ensure the claims your credential configuration needs are available from your identity provider.
Check the `claims_supported` and `scopes_supported` metadata at your provider's discovery endpoint.
See [ensuring claim availability](/docs/issuance/authorization-code/authentication-provider/overview#ensuring-claim-availability).
### Claims source [#claims-source]
A [claims source](/docs/issuance/claims-source/overview) is an external HTTPS endpoint that MATTR VII
calls during issuance to fetch additional claim data. This is useful when credential data lives in
systems separate from your identity provider.
**Common use cases:**
* Government databases (license records, registry data).
* HR systems (employee attributes, role assignments).
* CRM or customer databases (membership tiers, entitlements).
**How it fits in the workflow:**
1. Holder authenticates (Authorization Code) or presents offer (Pre-authorized Code).
2. MATTR VII calls your claims source endpoint with configured parameters.
3. Your endpoint returns JSON claim data.
4. Claims are mapped into the credential via the credential configuration.
**Integration requirements:**
* HTTPS endpoint reachable from MATTR VII (IPv4).
* Must respond within 3 seconds.
* Supports API key or OAuth client credentials for authorization.
* Must return claims as a flat or nested JSON object.
### Interaction hooks [#interaction-hooks]
An [interaction hook](/docs/issuance/authorization-code/interaction-hook/overview) adds a custom
step between authentication and credential issuance in the Authorization Code flow. This lets you
inject additional verification, consent collection, or data gathering.
**Common use cases:**
* Biometric or liveness verification.
* Additional consent or terms acceptance.
* Document upload or manual review steps.
* Custom data collection forms.
**How it fits in the workflow:**
1. Holder authenticates via the authentication provider.
2. MATTR VII redirects to your interaction hook URL with a signed session token.
3. Your component performs its logic (verification, consent, data collection).
4. Your component redirects back to MATTR VII with a signed response JWT containing any claims.
5. Returned claims become available for credential mapping.
Interaction hooks add latency and complexity. Use them only when the Authorization Code
flow's built-in authentication is insufficient for your assurance requirements.
## Next steps [#next-steps]
Next, [manage issuer keys and certificates](/docs/issuance/keys-and-certificates) so your credentials
are signed with keys your organization controls.
# Privacy in credential issuance
URL: /docs/issuance/privacy
Description: How MATTR VII handles claims, user data, and credential metadata during issuance, and what configuration choices control the data footprint of an issuance flow.
MATTR VII is the platform component that issues verifiable credentials. This page describes how
MATTR VII handles personal data during issuance, what is retained after a credential is delivered,
and the configuration choices that determine the data footprint of an issuance flow.
For the broader picture of MATTR's privacy stance, see
[Privacy in MATTR's architecture](/docs/concepts/privacy).
## Issuance is a transit operation [#issuance-is-a-transit-operation]
MATTR VII is designed as a transit platform for credential issuance. The role of the platform is
to take inputs from authoritative systems, produce a cryptographically signed credential, and
deliver it to the holder's wallet. It is not designed as a long-term store of credential content.
In practice, this means:
* Claims are fetched at issuance time from the configured
[Claims source](/docs/issuance/claims-source/overview) or supplied via the credential offer.
* The claims are used to populate the credential and to compute the cryptographic signature.
* The signed credential is delivered to the holder.
* The claim values are not persisted by default.
The issuer's system of record remains the authoritative source for the underlying data. MATTR VII
does not become a parallel store of credential content.
## What MATTR VII retains by default [#what-mattr-vii-retains-by-default]
To support audit, billing, and lifecycle operations such as revocation, MATTR VII retains a
limited set of metadata for each credential it issues. By default, this includes:
* A reference to the [User](/docs/issuance/users/overview) the credential was issued to.
* The [credential configuration](/docs/issuance/credential-configuration/overview) used.
* A hash of the issued credential (for mDocs, a hash of the MSO).
* The device public key bound to the credential.
* Issuance timestamps and status.
This metadata does not include the actual claim values inside the credential. It is enough to
support credential lifecycle operations without recreating the data minimization properties the
architecture is designed to provide.
If the customer wants to track which users have been issued which credentials, the
[User](/docs/issuance/users/overview) object is the intended hook. The claims map on a user is
deliberately small and is intended for pseudonymous identifiers (for example, an external user ID
that maps back to the customer's system of record), not for storing the credential content
itself.
The contents of the `claims` object on a User are persistent. Avoid including sensitive personal
information here. Best practice is to store a stable external identifier and look up any other
data in the customer's system of record when needed.
## Claims sources [#claims-sources]
A [Claims source](/docs/issuance/claims-source/overview) is the customer-controlled endpoint that
MATTR VII calls during issuance to retrieve the data that will go into a credential. The platform
acts purely as a client of that endpoint:
* Requests are made over HTTPS using either an API key or OAuth client credentials.
* MATTR VII passes only the request parameters configured by the customer, which can include
identifiers from the authentication flow or the credential offer.
* The response is used to populate the credential being issued.
* The response is not retained beyond what is necessary to complete the issuance request.
The customer controls what data their claims source returns. The privacy principle for this
integration is the same one that applies elsewhere: return only the data needed for the credential
being issued, not the full record about the user.
## OID4VCI flows and user data [#oid4vci-flows-and-user-data]
The OID4VCI flows used by MATTR VII have different user-data implications. The
[Authorization Code flow](/docs/issuance/authorization-code/overview) involves authenticating the
user against the customer's authentication provider before the credential is issued. MATTR VII
stores the relationship between the MATTR VII user and the authentication provider subject (so
that future credentials can be associated with the same user), but it does not store the
authentication provider's identity attributes.
The [Pre-authorized Code flow](/docs/issuance/pre-authorized-code/overview) does not involve a
direct authentication interaction. The customer's backend has already established the user's
identity before generating the credential offer. The flow can pass a pre-authorized code and an
optional transaction code to bind the offer to a specific holder.
In both flows, the data minimization principle is the same: pass only what is needed to identify
the user for the issuance interaction. Anything else belongs in the customer's system of record.
## After issuance [#after-issuance]
After a credential is delivered to the holder's wallet, MATTR VII's involvement in the credential
ends, except for lifecycle operations the customer initiates:
* **Revocation** is supported by publishing [status lists](/docs/issuance/revocation/overview).
Status lists contain only an index and a binary revocation value. They do not contain personal
information.
* **Update** flows allow the holder's wallet to obtain an updated credential when one is
available. The update interaction follows the same data-handling rules as initial issuance.
MATTR VII does not observe where the credential is presented, by whom it is verified, or what
specific attributes are disclosed. There is no central log of credential usage at the issuer.
## Practical guidance for issuers [#practical-guidance-for-issuers]
* Use [Claims sources](/docs/issuance/claims-source/overview) to fetch data at issuance time
rather than uploading bulk datasets to the platform.
* Keep User `claims` to a pseudonymous external identifier where possible. Do not persist
sensitive personal information on the User object.
* Choose the regional cloud deployment that matches your data residency requirements at
onboarding. Customer data stays within the chosen AWS region except for the listed
sub-processors.
* Make sure your end-user privacy notice reflects what your issuance flow actually collects and
retains, and obtain consent from the user for anything you intend to persist. The architecture
supports a minimal footprint, and the customer-facing notice should describe what the deployment
actually does.
* Handle data subject requests through the [Users API](/docs/issuance/users/api-reference). The
[Get user](/docs/issuance/users/api-reference#retrieve-a-user) and
[Get user credentials](/docs/issuance/users/api-reference#retrieve-user-credentials-data)
endpoints return what MATTR VII holds about a given subject, and
[Delete user](/docs/issuance/users/api-reference#delete-a-user) removes that record to support
right-to-be-forgotten requests. Note that credentials already issued to the user remain valid
after the User record is deleted. If a right-to-be-forgotten request requires invalidating
those credentials, revoke them via the [status list](/docs/issuance/revocation/overview) as a
separate step.
* For dedicated cloud customers, configure analytics at Level 1 or Level 2. Level 3 (full event data) is only appropriate for
short-lived debugging and should be paired with a reduced retention period. See the platform
events registry for the available levels.
## Frequently asked questions [#frequently-asked-questions]
### Does MATTR VII store the claims contained in credentials it issues? [#does-mattr-vii-store-the-claims-contained-in-credentials-it-issues]
No, not by default. MATTR VII operates as a transit platform during issuance. Claims flow through
the platform to be signed into a credential and are then delivered to the holder. Claim values are
not persisted unless the customer has explicitly configured them to be.
### Can MATTR VII track how a credential is used after it is issued? [#can-mattr-vii-track-how-a-credential-is-used-after-it-is-issued]
No. Once a credential is delivered to the holder's wallet, MATTR VII has no visibility into where,
when, or how it is later presented. The decentralized architecture makes this technically
impossible from the issuer side.
### What information about issued credentials is retained? [#what-information-about-issued-credentials-is-retained]
Limited audit information is retained, such as a hash of the credential, the credential
configuration used, and a reference to the user the credential was issued to. The actual claim
values are not retained by default.
### How are claims sources protected? [#how-are-claims-sources-protected]
Claims sources are called over HTTPS using either an API key or OAuth client credentials. MATTR
VII fetches only the data required for the specific credential being issued, uses it to populate
the credential, and does not persist the response beyond what is needed to complete issuance.
### What if I need to retain claims for business reasons? [#what-if-i-need-to-retain-claims-for-business-reasons]
Selected attributes can be configured to be persisted on the MATTR VII user via the claims source
configuration. This is an explicit, opt-in choice. Best practice is to avoid persisting sensitive
personal information and to favor pseudonymous external identifiers over full identity attributes.
## Related reading [#related-reading]
* [Privacy in MATTR's architecture](/docs/concepts/privacy)
* [Selective disclosure](/docs/concepts/selective-disclosure)
* [Decentralized trust model](/docs/concepts/decentralized-trust-model)
* [Claims source](/docs/issuance/claims-source/overview)
* [Users](/docs/issuance/users/overview)
* [Revocation](/docs/issuance/revocation/overview)
# Learn how to setup role based access control for your MATTR VII tenant
URL: /docs/platform-management/access-control-tutorial
## Overview [#overview]
MATTR VII uses Role-Based Access Control (RBAC) to manage permissions and access within a tenant.
Each role grants access to specific capabilities, ensuring that users or clients only have access to
the functionalities they need. Refer to [Access control](/docs/platform-management/access-control) for more information.
In this tutorial you will learn how to manage access control for your tenants by assigning roles to
clients, either using the MATTR Portal or the Management API.
This tutorial covers managing access controls for **clients**.
If you want to manage access for users, see the [Inviting a user to a tenant](/docs/platform-management/portal#inviting-users) guide. That guide also explains how to assign roles to users.
Once a user logs into the Portal and selects a tenant, they will only have access to the capabilities associated with their assigned roles.
## Prerequisites [#prerequisites]
* You will need access to the MATTR Portal or client credentials (`client_id` and `client_secret`) to use the Management API.
## Tutorial steps [#tutorial-steps]
1. [Create a new tenant](#create-a-new-tenant): This tenant will be used to trial the RBAC
configuration.
2. [Create a new client](#create-a-new-client): We will create a new client for our new tenant, and
assign a specific set of permissions to the client.
3. [Test client permissions](#test-client-permissions): Finally we will use the new client
credentials to obtain an access token and make requests to different endpoints. This should
ensure the client permissions were properly configured.
### Create a new tenant [#create-a-new-tenant]
Start off by creating a new tenant, either using the MATTR Portal or the Management API.
1. Select the **Create/switch tenant** button on the top-right side of the screen.\
The *All tenants* panel is displayed, listing any existing tenants.
2. Select the **Create new** button.\
The *New tenant* form is displayed.
3. Use the *Region* dropdown list to select the region your tenant will be hosted in.
4. Use the *Tenant subdomain* text box to insert a subdomain for your tenant (e.g. `access-control-tenant`).
5. Use the *Tenant name* text box to insert a meaningful and friendly name for your tenant (e.g. *Access Control Tutorial Tenant*).
6. Select the **Create** button to create the new tenant.
7. Copy the displayed tenant information and record it somewhere safe for future reference.
1. Make a request of the following structure to [obtain a Management API access token](/docs/api-reference/management/security/authToken):
```http title="Request"
POST https://auth.manage.au01.mattr.global/oauth/token
```
```json title="Request Body"
{
"client_id": "F5qae*************************",
"client_secret": "Wzc8J**********************************************************",
"audience": "https://manage.au01.mattr.global",
"grant_type": "client_credentials"
}
```
* `client_id` : Replace with the `client_id` value from your Management API client credentials.
* `client_secret` : Replace with the `client_secret` value from your Management API client
credentials.
* `audience` : Use `https://manage.au01.mattr.global` as per the example above.
* `grant_type` : Use `client_credentials` as per the example above.
These are not the same `client_id` and `client_secret` you were provided for
accessing the MATTR VII Platform API, but rather unique credentials for
accessing the Management API. If you have not received these credentials or
have any questions, please [contact us](mailto:dev-support@mattr.global)
before proceeding.
*Response*
```json title="Response body"
{
"access_token": "eyJhb********************************************************************", // [!code focus]
"expires_in": 14400,
"token_type": "Bearer"
}
```
2. Use the returned `access_token` must be used as a bearer token for all requests to the Management API in
the next steps to make a request of the following structure to
[create a new tenant](/docs/api-reference/management/tenants/createTenant):
```http title="Request"
POST https://manage.au01.mattr.global/v1/tenants
```
```json title="Request body"
{
"name": "Access Control Tutorial Tenant",
"subdomain": "access-control-tenant",
"environmentId": "fa605282-0223-4ae0-831d-af368bc39a55"
}
```
* `name` : The name that will be used to identify this tenant.
* `subdomain` : The subdomain that will be used to access this tenant.
* `environmentId` : The unique identifier of the environment where the new tenant will be created.
You can make a request to
[retrieve all environments](/docs/api-reference/management/environments/getEnvironments) you have
access to and use the `id` value from the response as the `environmentId`.
*Response*
```json title="Response body"
{
"id": "6facbcef-66cd-4a06-89e3-e44a4fc12000", // [!code highlight]
"name": "Access Control Tutorial Tenant", // [!code highlight]
"subdomain": "access-control-tenant.vii.au01.mattr.global", // [!code highlight]
"environment": {
// [!code highlight]
"id": "fa605282-0223-4ae0-831d-af368bc39a55",
"name": "Public Australia Sydney",
"domain": "vii.a01.mattr.global",
"deploymentModel": "public",
"authorizationServerDomain": "auth.manage.au01.mattr.global",
"region": {
"id": "0fd6ce12-a983-41d0-aca8-03e1bb6f6000",
"name": "au01",
"displayName": "Sydney, Australia"
}
},
"client": {
// [!code highlight]
"clientId": "MjQx108p***************FlwJQjy",
"clientSecret": "NanfSkVr**********************PfD3zJ"
}
}
```
* `id` : Globally unique tenant identifier.
* `name` : As provided in the request.
* `subdomain` : The tenant URL, constructed with the `subdomain` value provided in the request.
* `environment` : Indicates data for the environment in which the new tenant was created.
* `client` : Indicates the `clientId` and `clientSecret` for the default client created for this
tenant. This client is assigned an
[Admin](/docs/platform-management/access-control#tenant-admin-permissions) role by default,
meaning it has access to all endpoints in the tenant.
### Create a new client [#create-a-new-client]
We now have a tenant and a default admin client. Next we will create a client that will have an
Issuer role, enabling them to only access a subset of the tenant endpoints and capabilities.
1. Open **Platform Management** in the left navigation panel and select **Tenant**.\
The tenant management screen appears.
2. Click **Create/Switch Tenant** at the top-right.\
The All Tenants window opens.
3. Click **Switch** next to the tenant created in the previous step.
4. Under **Platform Management**, select **Users, clients & roles**.
5. Go to the **Clients** tab and click **Create new**.
6. Enter a name to identify the client in the *Name* field (e.g. *Tutorial Issuer client*).
7. Select the *Issuer* checkbox in the *Tenant access* section.
8. Click **Create** to create the client
9. Make note of the displayed client credentials (`auth_url`, `tenant_url`, `client_id` and
`client_secret`) as you will need them in the next step.
The `client_secret` is only displayed immediately after the client is created.
Once you navigate away from this screen, the client secret will be masked and
cannot be retrieved again. Ensure you save it securely at this point.
Make a request of the following structure to
[create a new tenant client](/docs/api-reference/management/clients/createTenantClient):
```http title="Request"
POST https://manage.au01.mattr.global/v1/tenants/{tenantId}/clients
```
* `tenantId` : Replace with the `id` identifying the tenant, obtained from the previous step's
response. This creates the client in the context of a specific tenant.
```json title="Request Body"
{
"name": "Tutorial Issuer client",
"roles": ["issuer"]
}
```
* `name` : Name of the client associated with this tenant.
* `roles` : An array of [roles](/docs/platform-management/access-control#role-permissions) assigned
to this client based on the capabilities it needs to access. In our example we are assigning it
the role of an [Issuer](/docs/platform-management/access-control#issuer-permissions).
*Response*
```json title="Response body"
{
"clientId": "suC7I*******************************",
"clientSecret": "Qn_43J****************************************************",
"name": "Example client",
"permissions": ["permission_1", "permission_2", "permission_3"],
"roles": ["issuer"]
}
```
* `clientId` : Client identifier for retrieving a tenant access token.
* `clientSecret` : Client secret for retrieving a tenant access token.
* `name` : As provided in the request.
* `permissions` : An array of permissions assigned to the client based on the defined `roles`.
* `roles` : As provided in the request.
### Test client permissions [#test-client-permissions]
You have successfully created a new client with a limited set of
[permissions](/docs/platform-management/access-control#issuer-permissions) relevant to credential
issuance. We can now test these permissions work as expected.
Regardless of whether you created the client using the MATTR Portal or the Management API, you should
have the `clientId` and `clientSecret` values available to you. You will need these to obtain an access
token for the new client.
**Step 1: Obtain access token**
Use the new client's `clientId` and `clientSecret` obtained in the previous step to obtain an access token for accessing the tenant:
```http title="Request"
POST https://auth.au01.mattr.global/oauth/token/oauth/token
```
If you created your tenant in a different environment you might need to use a
different authentication server. Please [contact
us](mailto:dev-support@mattr.global) if you are unsure.
```json title="Request body"
{
"client_id": "F5qae*******************************",
"client_secret": "Wzc8J**********************************************************",
"audience": "access-control-tenant.vii.au01.mattr.global",
"grant_type": "client_credentials"
}
```
* `client_id` : Replace with the `clientId` value obtained in the previous step.
* `client_secret` : Replace with the `clientSecret` value obtained in the previous step.
* `audience` : Replace with the `subdomain` obtained when you created the tenant.
* `grant_type` : Use `client_credentials` as per the example above.
*Response*
```json title="Response body"
{
"access_token": "eyJhb********************************************************************", // [!code focus]
"expires_in": 14400,
"token_type": "Bearer"
}
```
The returned `access_token` will enable accessing endpoints as per the `Issuer` role assigned to the
client. Let's test this works as expected.
**Step 2: Make a request to an endpoint the client should have access to**
Use the `access_token` obtained in the previous step and make a request of the following structure
to [create a new IACA](/docs/issuance/certificates/api-reference/iaca#create-an-iaca):
```http filename:"Request"
POST https://access-control-tenant.vii.au01.mattr.global/v2/credentials/mobile/iacas
```
```json filename:"Request body"
{
"commonName": "Example IACA",
"country": "US",
"stateOrProvinceName": "US-AL",
"notBefore": "2024-09-26",
"notAfter": "2034-09-26"
}
```
As a client with an `Issuer` role is expected to have access to this endpoint (used to create new
[IACA](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca) certificates), you should
receive a successful `201` response.
**Step 3: Make a request to an endpoint the client should not have access to**
Use the `access_token` obtained in the previous step and make a request of the following structure
to [create a new Ecosystem](/docs/issuance/certificates/api-reference/iaca#create-an-iaca):
```http title="Request"
POST https://access-control-tenant.vii.au01.mattr.global/v1/ecosystems
```
```json title="Request body"
{
"name": "My Ecosystem"
}
```
As a client with an `Issuer` role is not expected to have access to this endpoint (used to create
new [Ecosystems](/docs/digital-trust-service)), you should receive a `403` (Forbidden)
error.
## Summary [#summary]
In this tutorial you've learnt how to manage access control for your tenants by assigning roles to
clients, either using the MATTR Portal or the Management API.
You can now use a similar approach to assign different roles to different tenant clients as per your
implementation requirements and security practices.
# Access control
URL: /docs/platform-management/access-control
## Overview [#overview]
MATTR VII uses **Role-Based Access Control (RBAC)** to manage permissions and access within a
tenant. Each role grants access to specific capabilities, ensuring that users or clients only have
access to the functionalities they need. Below is a list of available roles and their descriptions:
* [Tenant admin](#tenant-admin-permissions) (`admin`): Has full access to all tenant capabilities.
This role is assigned to the default client when a new tenant is created.
* [Issuer](#issuer-permissions) (`issuer`): Has access to capabilities required for issuing and
managing credentials of different formats across different channels.
* [Managed Issuer](#managed-issuer-permissions) (`managed-issuer`): Has access to capabilities required for
issuing and managing credentials within a managed issuance setup. The main difference between this role
and the *Issuer* role is that users or clients with the *Managed Issuer* role cannot create new IACAs or manage Credential configurations.
* [Verifier](#verifier-permissions) (`verifier`): Has access to capabilities required for verifying
credentials of different formats across different channels.
* [DTS provider](#dts-provider-permissions) (`dts-provider`): Has access to capabilities required
for managing a Digital Trust Service (DTS).
* [DTS consumer](#dts-consumer-permissions) (`dts-consumer`): Has access to capabilities required to
consume DTS information from a tenant.
* [Auditor](#auditor-permissions) (`auditor`): Has read-only access to analytics data.
## Role permissions [#role-permissions]
The following sections detail the capabilities available for different roles. For an inclusive list
of the endpoints and operations each role can access, please refer to the
[roles and permissions list](/docs/platform-management/roles-and-permissions).
Furthermore, the MATTR VII [API reference](/docs/api-reference) details, for each endpoint, what roles
can access it.
### Tenant admin permissions [#tenant-admin-permissions]
The following list details the MATTR VII capabilities available to users and clients assigned with
the *Tenant admin* role. This includes all tenant capabilities:
**Platform management**
* Manage [Custom domain](/docs/platform-management/custom-domain-overview)
* Manage [DIDs](/docs/concepts/dids)\*
* Manage [IACAs](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca)
* Manage [Webhooks](/docs/platform-management/webhooks-overview)
* Monitor [Analytics](/docs/platform-management/analytics/overview) information
* Manage Messaging\*
* Manage [Tenants](/docs/platform-management/management-api/overview#getting-started)
**Digital Trust Service**
* Manage [Ecosystems](/docs/digital-trust-service)
* Manage Ecosystem
[participants](/docs/digital-trust-service/vical-guide#create-participants-and-add-issuer-certificates)
* Manage Ecosystem
[credential types](/docs/digital-trust-service/vical-guide#create-participants-and-add-issuer-certificates)
* Manage Ecosystem [policies](/docs/digital-trust-service/vical-guide#manually-publish-a-vical)
**Credential issuance**
* [OID4VCI](/docs/issuance/oid4vci-overview):
* Manage [Authentication providers](/docs/issuance/authorization-code/authentication-provider/overview)
* Manage [Interaction hooks](/docs/issuance/authorization-code/interaction-hook/overview)
* Manage [Claim sources](/docs/issuance/claims-source/overview)
* Manage [Credential configurations](/docs/issuance/credential-configuration/overview)
* Creating [Credential offers](/docs/issuance/credential-offer/overview)
* [Direct issuance](/docs/issuance/direct-issuance-overview) of [CWT](/docs/issuance/cwt-direct-issuance) credentials\*
* Managing [PDF](/docs/issuance/cwt-credential-templates/pdf-templates),
[Apple](/docs/issuance/cwt-credential-templates/apple-templates) and
[Google](/docs/issuance/cwt-credential-templates/google-templates) Digital Pass templates for CWT and Semantic
CWT credentials\*
**Credential management**
* [Revoking](/docs/issuance/revocation/overview) CWT, Semantic CWT and mDoc credentials\*
**Credential verification**
* Verification of [mDocs](/docs/verification/remote-overview),
[CWT](/docs/api-reference/platform/cwt-credentials-verification/verify-compact-credential)
and
[Semantic CWT](/docs/api-reference/platform/semantic-cwt-credentials-verification/verifyCompactSemantiCredential)
credentials\*
* Configure workflows for remote (online) verification of [mDocs](/docs/verification/remote-overview).
* Manage
[Reader certificates](/docs/verification/remote-web-verifiers/workflow#the-mattr-vii-tenant-returns-the-request-object)
***
### Issuer permissions [#issuer-permissions]
The following list details the MATTR VII capabilities available to users and clients assigned with
the *Issuer* role:
**Platform management**
* Manage [Custom domain](/docs/platform-management/custom-domain-overview)
* Manage [DIDs](/docs/concepts/dids)\*
* Manage [IACAs](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca)
* Manage [Webhooks](/docs/platform-management/webhooks-overview)
**Digital Trust Service**
* Retrieve Ecosystem [policies](/docs/digital-trust-service/vical-guide#manually-publish-a-vical)\*
**Credential issuance**
* [OID4VCI](/docs/issuance/oid4vci-overview):
* Manage [Authentication providers](/docs/issuance/authorization-code/authentication-provider/overview)
* Manage [Interaction hooks](/docs/issuance/authorization-code/interaction-hook/overview)
* Manage [Claim sources](/docs/issuance/claims-source/overview)
* Manage [Credential configurations](/docs/issuance/credential-configuration/overview)
* Creating [Credential offers](/docs/issuance/credential-offer/overview)
* [Direct issuance](/docs/issuance/direct-issuance-overview) of [CWT](/docs/issuance/cwt-direct-issuance) credentials\*
* Managing [PDF](/docs/issuance/cwt-credential-templates/pdf-templates),
[Apple](/docs/issuance/cwt-credential-templates/apple-templates) and
[Google](/docs/issuance/cwt-credential-templates/google-templates) Digital Pass templates for CWT and Semantic
CWT credentials\*
**Credential management**
* [Revoking](/docs/issuance/revocation/overview) CWT, Semantic CWT and mDoc credentials\*
***
### Managed Issuer permissions [#managed-issuer-permissions]
The following list details the MATTR VII capabilities available to users and clients assigned with
the *Managed Issuer* role:
**Platform management**
* Manage [Custom domain](/docs/platform-management/custom-domain-overview)
* Manage [DIDs](/docs/concepts/dids)\*
* Manage [Webhooks](/docs/platform-management/webhooks-overview)
* Retrieve [IACAs](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca)
* Monitor [Analytics](/docs/platform-management/analytics/overview) information
**Digital Trust Service**
* Retrieve Ecosystem [policies](/docs/digital-trust-service/vical-guide#manually-publish-a-vical)\*
**Credential issuance**
* [OID4VCI](/docs/issuance/oid4vci-overview):
* Manage [Authentication providers](/docs/issuance/authorization-code/authentication-provider/overview)
* Manage [Interaction hooks](/docs/issuance/authorization-code/interaction-hook/overview)
* Manage [Claim sources](/docs/issuance/claims-source/overview)
* Creating [Credential offers](/docs/issuance/credential-offer/overview)
* [Direct issuance](/docs/issuance/direct-issuance-overview) of [CWT](/docs/issuance/cwt-direct-issuance) credentials\*
* Managing [PDF](/docs/issuance/cwt-credential-templates/pdf-templates),
[Apple](/docs/issuance/cwt-credential-templates/apple-templates) and
[Google](/docs/issuance/cwt-credential-templates/google-templates) Digital Pass templates for CWT and Semantic
CWT credentials\*
**Credential management**
* [Revoking](/docs/issuance/revocation/overview) CWT, Semantic CWT and mDoc credentials\*
***
### Verifier permissions [#verifier-permissions]
The following list details the MATTR VII capabilities available to users and clients assigned with
the *Admin* role:
**Platform management**
* Manage [Custom domain](/docs/platform-management/custom-domain-overview)
* Manage [DIDs](/docs/concepts/dids)\*
* Manage [Messaging](/docs/api-reference/platform/messaging/signMessage)\*
**Digital Trust Service**
* Retrieve Ecosystem [policies](/docs/digital-trust-service/vical-guide#manually-publish-a-vical)\*
**Credential verification**
* Verification of [mDocs](/docs/verification/remote-overview),
[CWT](/docs/api-reference/platform/cwt-credentials-verification/verify-compact-credential)
and
[Semantic CWT](/docs/api-reference/platform/semantic-cwt-credentials-verification/verifyCompactSemantiCredential)
credentials\*
* Configure workflows for remote (online) verification of [mDocs](/docs/verification/remote-overview).
* Manage
[Reader certificates](/docs/verification/remote-web-verifiers/workflow#the-mattr-vii-tenant-returns-the-request-object)
***
### DTS provider permissions [#dts-provider-permissions]
The following list details the MATTR VII capabilities available to users and clients assigned with
the *DTS provider* role:
**Platform management**
* Manage [Custom domain](/docs/platform-management/custom-domain-overview)
* Manage [DIDs](/docs/concepts/dids)\*
**Digital Trust Service**
* Manage [Ecosystems](/docs/digital-trust-service)
* Manage Ecosystem
[participants](/docs/digital-trust-service/vical-guide#create-participants-and-add-issuer-certificates)
* Manage Ecosystem
[credential types](/docs/digital-trust-service/vical-guide#create-participants-and-add-issuer-certificates)
* Manage Ecosystem [policies](/docs/digital-trust-service/vical-guide#manually-publish-a-vical)
***
### DTS consumer permissions [#dts-consumer-permissions]
The following list details the MATTR VII capabilities available to users and clients assigned with
the *DTS consumer* role:
**Digital Trust Service**
* Retrieve Ecosystem [policies](/docs/digital-trust-service/vical-guide#manually-publish-a-vical)\*
***
### Auditor permissions [#auditor-permissions]
The following list details the MATTR VII capabilities available to users and clients assigned with
the *Auditor* role:
**Platform management**
* Monitor [Analytics](/docs/platform-management/analytics/overview) information
***
*\* Partial support or not available for users using
[MATTR Portal](/docs/platform-management/portal); users or clients using
[MATTR VII API](/docs/api-reference/) are not affected.*
# Create a client for a tenant
URL: /docs/platform-management/create-client
A client represents an application or service that interacts with a tenant by making API requests. To
enable this interaction, you need to create a client and assign it appropriate roles based on the
capabilities it needs to access.
This can be achieved either via the MATTR Portal or by using the
[Management API](/docs/api-reference/management-api-reference-overview).
1. Open **Platform Management** in the left navigation panel and select **Tenant**.\
The tenant management screen appears.
2. Click **Create/Switch Tenant** at the top-right.\
The All Tenants window opens.
3. Click **Switch** next to the tenant created in the previous step.
4. Under **Platform Management**, select **Users, clients & roles**.
5. Go to the **Clients** tab and click **Create new**.
6. Enter a name to identify the client in the *Name* field (e.g. *Tutorial Issuer client*).
7. Select the required permissions in the *Tenant access* section.
8. Click **Create** to create the client
9. Make note of the displayed client credentials (`auth_url`, `tenant_url`, `client_id` and
`client_secret`).
The `client_secret` is only displayed immediately after the client is created.
Once you navigate away from this screen, the client secret will be masked and
cannot be retrieved again. Ensure you save it securely at this point.
Make a request of the following structure to
[create a new tenant client](/docs/api-reference/management/clients/createTenantClient):
```http title="Request"
POST https://manage.au01.mattr.global/v1/tenants/{tenantId}/clients
```
* `tenantId` : Replace with the `id` identifying the tenant, obtained from the previous step's
response. This creates the client in the context of a specific tenant.
```json title="Request Body"
{
"name": "Tutorial Issuer client",
"roles": ["issuer"]
}
```
* `name` : Name of the client associated with this tenant.
* `roles` : An array of [roles](/docs/platform-management/access-control#role-permissions) assigned
to this client based on the capabilities it needs to access. This example is assigning it
the role of an [Issuer](/docs/platform-management/access-control#issuer-permissions).
*Response*
```json title="Response body"
{
"clientId": "suC7I*******************************",
"clientSecret": "Qn_43J****************************************************",
"name": "Example client",
"permissions": ["permission_1", "permission_2", "permission_3"],
"roles": ["issuer"]
}
```
* `clientId` : Client identifier for retrieving a tenant access token.
* `clientSecret` : Client secret for retrieving a tenant access token.
* `name` : As provided in the request.
* `permissions` : An array of permissions assigned to the client based on the defined `roles`.
* `roles` : As provided in the request.
# API Reference
URL: /docs/platform-management/custom-domain-api-reference
## Create a Custom domain [#create-a-custom-domain]
## Retrieve Custom domain [#retrieve-custom-domain]
## Update Custom domain [#update-custom-domain]
## Delete Custom domain [#delete-custom-domain]
## Verify Custom domain [#verify-custom-domain]
# How to configure a Custom domain
URL: /docs/platform-management/custom-domain-guide
[Custom domains](/docs/platform-management/custom-domain-overview) represent your known and trusted brand, and can
assist in instilling trust with your end-users when they interact with your MATTR VII tenant.
## Prerequisites [#prerequisites]
* You must have an existing web domain.
* You must have control over this web domain DNS records.
* You must have a web service that runs on your web domain and can redirect or proxy requests.
## Overview [#overview]
Configuring a [custom domain](/docs/platform-management/custom-domain-overview) for your MATTR VII tenant comprises
the following steps:
1. [Create custom domain configuration](#create-custom-domain-configuration).
2. [Insert verification token into custom domain TXT record](#insert-verification-token-into-custom-domain-txt-record).
3. [Verify custom domain](#verify-custom-domain).
4. [Create redirects for required assets](#create-redirects-for-required-assets).
5. [Create a new `did:web` to identify the custom domain](#create-a-new-didweb-to-identify-the-custom-domain).
### Create a custom domain configuration [#create-a-custom-domain-configuration]
This step can be performed either using either using the MATTR Portal or by making an API request.
1. Expand the **Platform Management** menu in the navigation panel on the left-hand side.
2. Select **Custom domain**.
3. Enter a meaningful name in the *Name* textbox (e.g. "My Custom Domain").
4. Insert a suitable URL in the *Logo* URL textbox (e.g. `https://my-custom.site/logo.png`):
* URL must be publicly available.
* Must be a square image.
* png and jpg files are supported.
* Recommended size is 64x64 px.
* Recommended maximum size is 15 KB.
5. Insert your full custom domain in the *Domain* textbox, leaving out the protocol (e.g.
`my-custom.site`).
6. Select **Create**.\
Your Custom domain record is created and you must now verify it.
7. Copy the *DNS entry TXT record* value from the displayed record. You will need it in the next step.
Make a request of the following structure to
[configure your custom domain](/docs/platform-management/custom-domain-api-reference#create-a-custom-domain):
```http title="Request"
POST /v1/config/domain
```
```json title="Request body"
{
"name": "Custom Site",
"logoUrl": "https://my-custom.site/logo-social.png",
"domain": "my-custom.site"
}
```
* `name` : Insert a name for the custom domain that will be displayed to digital wallet holders when
they receive credential offers or verification requests from your MATTR VII tenant.
* `logoUrl` : Insert a URL for a logo that will be displayed to digital wallet holders when they
receive credential offers or verification requests from your MATTR VII tenant:
* URL must be publicly available.
* Must be a square image.
* png and jpg files are supported.
* Recommended size is 64x64 px.
* Recommended maximum size is 15 KB.
* `domain` : Insert the full custom domain, leaving out the protocol (e.g. https\://).
*Response*
```json title="Response body"
{
"name": "Custom Site",
"logoUrl": "https://my-custom.site/logo.png",
"domain": "my-custom.site",
"verificationToken": "a45ba33b-6c47-4349-7d49-a516c4d38406", // [!code focus]
"isVerified": false // [!code focus]
}
```
* `verificationToken` : This value must be added to your domain DNS entry TXT record in
[step 2](#insert-verification-token-into-custom-domain-txt-record).
* `isVerified` : This will indicate `false` until the domain has been verified as described in
[step 3](#verify-custom-domain). MATTR VII will only use the custom domain after it has been
verified and this field indicates `true`.
Once the configuration is created, it is added to your tenant's `manifest.json` file. This is one of
the files you will need to [create redirects](#create-redirects-for-required-assets) to.
### Insert verification token into custom domain TXT record [#insert-verification-token-into-custom-domain-txt-record]
Insert the DNS entry TXT record (`verificationToken`) obtained in [step 1](##create-custom-domain-configuration) into a
TXT record in your custom domain DNS entry.
The exact steps to perform this will vary depending on your DNS service providers:
* [Cloudflare](https://developers.cloudflare.com/dns/manage-dns-records/how-to/create-dns-records/)
* [GoDaddy](https://www.godaddy.com/en/help/add-a-txt-record-19232)
* [AWS](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resource-record-sets-creating.html)
* [Google Cloud](https://cloud.google.com/dns/docs/records)
* [Entrust](https://www.entrust.com/knowledgebase/ssl/how-to-add-txt-record-for-dns-verification-on-hover)
* [Akamai Edge](https://learn.akamai.com/en-us/webhelp/edge-dns/edge-dns-user-guide/GUID-0033207B-AC09-44DF-8022-EC62ACAEB203.html)
**Troubleshooting**
If you can't set a TXT record for your custom domain because it already has a CNAME record, you can
workaround this issue by performing the following:
1. Remove the CNAME record.
2. Add a TXT record with the correct verificationToken.
3. Verify your custom domain.
4. Remove the TXT record.
5. Return the CNAME record.
### Verify custom domain [#verify-custom-domain]
Once your DNS provider has registered your TXT value and the change has been propagated, MATTR VII
must reach out to the public DNS record and confirm the value matches the one set up on your tenant.
This step can also be performed either using either using the MATTR Portal or by making an API request.
1. Expand the **Platform Management** menu in the navigation panel on the left-hand side.
2. Select **Custom domain**.
3. Select the **Verify** button in the Verification status panel.\
MATTR VII will attempt to verify your custom domain by checking the TXT record you created in
[step 2](#insert-verification-token-into-custom-domain-txt-record). Upon successful verification, the displayed status should change from *Not verified* to *Verified*.
Make a request of the following structure to verify your custom domain:
```http title="Request"
POST /v1/config/domain/verify
```
*Response*
* `204` : (with no body): Verification successful! Your custom domain is now active!
* `400` : Something has gone wrong with the DNS setup. It could be that you need to wait longer to
ensure your TXT record has propagated or repeat the process to verify domain ownership.
* `404` : No custom domain configured on the tenant. Return to
[step 1](#create-custom-domain-configuration) and configure a custom domain.
### Create redirects for required assets [#create-redirects-for-required-assets]
For your custom domain to function properly, you will need to setup redirects to assets that are
hosted on your MATTR VII tenant. Different redirect are required based on features used as part of
your implementation:
| Asset path | Description | Required |
| :---------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------- |
| `/manifest.json` | This file is required to provide digital wallet clients with information about your custom domain, such as its name and logo. | Always |
| `/.well-known/did.json` | This is the `did:web` [document](/docs/concepts/dids#did-document) associated with your domain. It must be available for your `did:web` to be [resolvable](/docs/concepts/dids#resolving). | Only when issuing CWT credentials |
| `/.well-known/did-configuration` | This file is used to establish trust in the [DID to domain linkage](/docs/concepts/dids#verification-relationships). This will ensure any digital wallet clients can resolve DIDs from your custom domain and verify the DID-to-domain linkage. | Only when issuing CWT credentials |
| `/.well-known/openid-credential-issuer` | This file includes details required for interacting with your tenant as part of an [OID4VCI workflow](/docs/issuance/oid4vci-overview). | Only when issuing credentials using the OID4VCI workflow |
| `/.well-known/oauth-authorization-server` | This file includes details required for interacting with your tenant as an OAuth authorization server as part of an [OID4VCI workflow](/docs/issuance/oid4vci-overview). | Only when issuing credentials using the OID4VCI workflow |
| `/.well-known/oauth-client` | This file includes details required for interacting with your tenant as an OAuth client as part of an [mDocs online verification workflow](/docs/verification/remote-overview). | Only when verifying mDocs remotely |
For example, if we were to set up `example.com` as a custom domain for the Learn MATTR VII tenant,
we would need to create the following redirect for `/manifest.json` :
| Origin | Redirect target |
| :---------------------------------- | :-------------------------------------------------- |
| `https://example.com/manifest.json` | `https://learn.vii.au01.mattr.global/manifest.json` |
Once this redirect is setup, users attempting to interact with the custom domain will be served the
asset from the Learn MATTR VII tenant.
If any of the redirect paths are already used by your main website, you may
need to create a separate subdomain for your MATTR VII custom domain.
### Create a new did:web to identify the custom domain (Optional) [#create-a-new-didweb-to-identify-the-custom-domain-optional]
This step is only required if you are using your MATTR VII tenant to issue
[CWT](/docs/concepts/cwt) credentials.
When using your MATTR VII tenant to issue [CWT](/docs/concepts/cwt) credentials you
must have a publicly available and resolvable [`did:web`](/docs/concepts/dids#didweb) that identifies
this tenant as an issuer.
When configuring a custom domain, you must create a new `did:web` that references the URL of your
custom domain instead of your actual MATTR VII tenant. This means that when holders attempt to claim
credentials issued by your MATTR VII tenant, they will see your custom domain as the issuer's
identifier.
[Create a did:web](/docs/api-reference/platform/dids/createDid) and use your custom
domain as the `url` in the request body.
MATTR VII will automatically host the generated DID document on
your tenant, and make it available for any relying party attempting to
[resolve](/docs/concepts/dids#resolving) this `did:web`.
# Custom domain
URL: /docs/platform-management/custom-domain-overview
You can configure a custom domain for your MATTR VII tenant to represent your brand and instil trust
with your end-users. For example, end-users might not feel comfortable claiming a credential issued
from your MATTR VII tenant URL (e.g. `https://learn.vii.au01.mattr.global`), as they don’t know who
or what MATTR is. Using your own domain might be a better approach.
Custom domains don’t change how you interact with your tenant for administration functions and don’t
prevent the existing tenant domain from being accessed.
Since MATTR VII tenants are not exposed to end-users directly, the custom domains model we have
implemented does not require full DNS mapping of a custom domain to a tenant (i.e. CNAME or A
record). Instead, we allow setting a custom domain and then proving ownership using a TXT record on
the DNS record.
## Requirements [#requirements]
* Any MATTR VII tenant can only be linked to one custom domain.
* You must be on a MATTR VII paid plan.
* You must have an existing web domain.
* You must have control over this web domain DNS records.
* You must have a web service that runs on your web domain and can redirect or proxy requests.
## Process overview [#process-overview]
Setting up a custom domain for your MATTR VII tenant comprises the following steps:
1. [Configure a custom domain](#configure-a-custom-domain).
2. [Verify domain ownership](#verify-domain-ownership).
3. [Verify your custom domain](#verify-your-custom-domain).
4. [Create required redirects](#create-required-redirects).
### Configure a custom domain [#configure-a-custom-domain]
The first step is to make an API request to
[create a custom domain configuration](/docs/platform-management/custom-domain-guide#create-custom-domain-configuration)
on your MATTR VII tenant. This configuration defines the following:
* Custom domain name (this is displayed to holders as they interact with your tenant).
* Custom domain logo (this is displayed to holders as they interact with your tenant).
* The domain the custom domain is hosted on. You may choose to create a separate subdomain from
your main web presence to expose to end-users as part of managing their digital identities, or
you may choose to run from your existing main website. The choice may come down to practical
implementation details as you will need to setup [redirects](#create-required-redirects) to
certain paths. If these paths are already or likely to be used by your main website, you may
want to consider using them under a subdomain.
Once the configuration is created, it is added to your tenant's `manifest.json` file. This is one of
the files you will need to [create redirects](#create-required-redirects) to.
### Verify domain ownership [#verify-domain-ownership]
After configuring your custom domain on MATTR VII, you must provide proof of ownership over the
configured domain. To do this you must insert the verificationToken (obtained from the configuration
response) into a TXT record in your custom domain DNS entry.
### Verify your custom domain [#verify-your-custom-domain]
Once your DNS provider has registered your TXT value and the change has been propagated, MATTR VII
must reach out to the public DNS record and confirm the value matches the one set up on your tenant.
### Create required redirects [#create-required-redirects]
For your custom domain to function properly, you will need to setup redirects to assets that are
hosted on your MATTR VII tenant. Different redirect are required based on features used as part of
your implementation. Refer to the [Custom Domain guide](/docs/platform-management/custom-domain-guide#create-redirects-for-required-assets) for more information.
# Environments and tenants
URL: /docs/platform-management/environments-and-tenants
## Overview [#overview]
MATTR VII is designed to support customers of all sizes with a flexible, secure, and scalable digital trust platform.
At its core, MATTR VII enables multiple customers to operate independently and securely through a concept called **multi-tenancy**. Each customer operates in its own isolated space (a **tenant**), ensuring its data, credentials, and configuration remain private and protected.
Depending on operational, compliance, and change management requirements, customers can choose between:
* A **Standard Cloud deployment**, where infrastructure is securely and efficiently shared.
* A **Dedicated Cloud deployment**, where an environment is provisioned exclusively for a single customer.
This flexible model allows customers to balance cost efficiency, scalability, regulatory compliance, and operational control without compromising trust.
## What is a Tenant? [#what-is-a-tenant]
MATTR VII implements a multi-tenant architecture, where a single logical platform instance serves multiple tenants.
A tenant represents a distinct and isolated instance of MATTR VII within an environment.
Each tenant maintains its own isolated:
* **Data**: Credentials, presentations, and verification records
* **Configuration**: Certificates, credential configurations, policies, and governance rules
* **Access control**: OAuth clients, API keys, and permissions
* **Operational state**: Activity logs, analytics, and audit trails
Every Platform API request occurs within the context of a specific tenant.
**Tenants are fully isolated**. No tenant can access another tenant's data, configuration, or credentials. A tenant in one environment cannot interact with or discover tenants in another environment.
Isolation is enforced at both:
* **Functional levels**: Ensuring strict data and access separation
* **Non-functional levels**: Supporting request affinity and workload controls
This ensures strong security boundaries while still enabling efficient shared infrastructure.
### Shared vs Isolated: What's the Difference? [#shared-vs-isolated-whats-the-difference]
Understanding what is shared at the environment level versus what is isolated at the tenant level is key to understanding MATTR VII's architecture:
**Shared at Environment Level** (applies to all tenants in the environment):
* Platform version and software release
* Core features and capabilities
* Infrastructure and compute resources
* API endpoints and service architecture
* Platform-wide configuration settings (e.g. rate limits, logging levels)
**Isolated at Tenant Level** (unique to each tenant):
* Credentials, Certificates, and cryptographic keys
* Issued credentials and presentation records
* Credential configurations and templates
* Governance policies and verification rules
* OAuth clients and API access controls
* User data and activity logs
* Custom domains and branding
This separation enables MATTR VII to deliver consistent platform functionality and efficient resource utilisation, while maintaining absolute data isolation and security between tenants.
## What is an Environment? [#what-is-an-environment]
An environment is a deployment boundary for MATTR VII.
An environment includes:
* MATTR VII platform services
* The MATTR Portal (one per environment)
* Token endpoints and APIs
* Supporting infrastructure
* One or more tenants
**Multiple** tenants can exist within a **single** environment.
All tenants within an environment share:
* **Platform version**: The same MATTR VII software release
* **Feature availability**: The same core platform capabilities and features. Feature flags may exist on a tenant or environment level, but the underlying platform functionality is consistent across tenants in the same environment.
* **Infrastructure**: Compute resources, network architecture, and platform services
* **Release schedule**: Updates and patches are applied to the environment as a whole
While tenants share certain environment-level characteristics, they remain fully isolated from each other in terms of data, credentials, and tenant-specific configurations. Environments enable customers to separate workloads, manage release promotion, and meet operational requirements. Environments can also be geographically separated to support regional, regulatory, or performance needs.
## Deployment Models [#deployment-models]
MATTR VII supports two primary cloud deployment models: Standard Cloud and Dedicated Cloud.
### Standard Cloud [#standard-cloud]
In the Standard Cloud model, customers receive a tenant in one of MATTR’s shared environments (deployed in a specific AWS region). Each environment hosts multiple tenants, with shared underlying infrastructure.
Available regions currently include:
* US West (Oregon)
* Canada (Central)
* Europe (Frankfurt)
* Asia Pacific (Singapore)
* Asia Pacific (Sydney)
* Asia Pacific (New Zealand)
What is **Shared**?
* Underlying infrastructure
* Compute resources
* Platform services
What is **Isolated**?
* Tenant data
* Credentials
* Configuration
* Access control policies
* Governance rules
All tenants receive the same core MATTR VII product functionality within the environment. Feature flags and configuration may influence what is enabled for a particular tenant.
### Dedicated Cloud [#dedicated-cloud]
Dedicated Cloud is MATTR’s solution for customers who require a dedicated environment and/or customer-approved and coordinated change management.
Dedicated Cloud includes:
* One or more environments deployed in any AWS region
* All customer data isolated within an individual AWS account that is not shared with other customers
* Compute resources allocated to the Dedicated Cloud environment and shared only across that customer’s tenants
Within each environment:
* Multiple tenants can be created
* Tenants remain fully isolated from one another
* MATTR VII and MATTR Portal are deployed and released into that environment
Dedicated Cloud includes enhanced operational controls such as:
* Configurable platform event logging levels
* Integration with customer SIEM solutions
These capabilities provide improved visibility and alignment with enterprise security monitoring requirements.
## Isolating deployment stages [#isolating-deployment-stages]
Most organisations need to separate their development, testing, and production workloads to reduce risk and manage changes safely. How you achieve this depends on your deployment model.
### Standard Cloud [#standard-cloud-1]
Standard Cloud customers operate within a single shared environment and cannot provision separate environments. Instead, use **separate tenants** to represent each stage of your software development lifecycle.
A common pattern is to use distinct tenants for:
* Development
* QA / testing
* Staging
* Production
At a minimum, it is recommended to keep non-production and production workloads in separate tenants. Because each tenant is fully isolated — with its own credentials, configurations, access controls, and data — changes made in a development or staging tenant cannot affect your production tenant. Teams can maintain separate API client credentials and access controls per stage, and safely validate credential configurations and policies before promoting them.
Note that all tenants within a Standard Cloud environment share the same platform version and release schedule. If your organisation requires independent release management or coordinated change control across stages, consider Dedicated Cloud.
### Dedicated Cloud [#dedicated-cloud-1]
Dedicated Cloud supports provisioning **multiple environments** within a single customer account, enabling a traditional model of promoting changes progressively across distinct stages — for example, Development, QA, Staging, and Production.
Each environment operates independently and can run at a different software version, giving teams full control over when platform updates are adopted at each stage. Changes validated in a lower environment can be promoted to the next, reducing risk before they reach production.
## Custom Domains [#custom-domains]
Custom domains in MATTR VII allow organisations to present a trusted, branded experience to end-users, while maintaining secure and structured platform architecture.
Custom domains can be applied at two levels:
* Per Tenant (Custom Domain)
* Per Environment (Environment Domain, Dedicated Cloud only)
These serve different purposes and operate differently.
### Tenant-Level Custom Domains [#tenant-level-custom-domains]
A tenant-level custom domain allows you to represent your brand when interacting with end-users.
For example, an end-user may hesitate to claim a credential issued from a MATTR VII tenant URL such as `https://learn.vii.au01.mattr.global`, because the end-user may not recognise MATTR as the issuing authority.
By configuring a custom domain (for example, `https://credentials.yourorganisation.com`), you can:
* Reinforce brand recognition
* Increase user confidence
* Provide a seamless, trusted experience
* Align credential flows with your organisation’s identity
Refer to our [Custom Domain Overview](/docs/platform-management/custom-domain-overview) for more details on tenant-level custom domains.
### Environment-Level Domains [#environment-level-domains]
Environment-level domains apply to the entire environment, not to an individual tenant. This configuration is typically used in dedicated cloud deployments.
When applied at the environment level, a domain can be configured for:
* The MATTR VII base API endpoint
* The OAuth token endpoint
* MATTR Portal
* DTS (Digital Trust Service) website
Unlike tenant-level custom domains, environment-level domains are part of the infrastructure and deployment configuration, rather than a branding mechanism for a specific tenant.
# Platform Management
URL: /docs/platform-management
MATTR platforms are designed to empower customers with the tools and flexibility to self-onboard, configure, and integrate in the way that best suits their needs. Whether you’re building programmatically with APIs or working through an intuitive web interface, you can manage your tenant securely and confidently.
At the heart of Platform Management are features that simplify administration, streamline integrations, and enable visibility and control:
* [Management APIs](/docs/platform-management/management-api/overview): A comprehensive set of management tools that allow you to create and manage tenants, control access, and define role-based permissions. These APIs provide a foundation for automation and seamless integration into your existing systems.
* [MATTR Portal](/docs/platform-management/portal): A graphical user interface built on the MATTR VII platform and Management APIs. The Portal provides a secure, role-based environment where administrators can easily configure integrations, manage tenant settings, and oversee platform operations—all without needing to write code.
* [Access Control](/docs/platform-management/access-control): Fine-grained controls to define who can access tenants and what actions they are permitted to perform. Role-based access ensures your teams have the right permissions while maintaining strong governance and security.
* [Analytics Tools](/docs/platform-management/analytics/overview): Built-in monitoring capabilities to track tenant activity, giving you insight into usage patterns and system behaviour. Analytics help you make informed decisions and maintain confidence in your platform operations.
* [Webhooks](/docs/platform-management/webhooks-overview): Event-driven integration that connects MATTR platforms to your wider business ecosystem. Configure Webhooks to respond to platform events in real-time, triggering downstream workflows and keeping systems in sync.
* [Custom Domain](/docs/platform-management/custom-domain-overview): Configure your tenant to operate under a domain that represents your brand. A custom domain not only instils trust with your end-users but also ensures a seamless and branded experience.
## Underlying platforms [#underlying-platforms]
All platform management capabilities are provided through MATTR VII APIs, accessible via the MATTR Portal or direct Management API requests.
# MATTR Portal
URL: /docs/platform-management/portal
## Overview [#overview]
The MATTR Portal is built on top of MATTR VII Platform and Management APIs and provides an easy-to-use
interface to effectively manage and oversee tenant management and configuration.
Not all features available via the MATTR VII Platform and Management APIs are
currently supported in the Portal. Refer to the [Capabilities](#capabilities)
section below for a list of supported functionalities.
## Getting Started [#getting-started]
### Creating a tenant [#creating-a-tenant]
Every action in the Portal happens within the context of a specific tenant. Tenants represent distinct
instances of MATTR VII, each with its own configuration, credentials, and governance. When you sign
in to the Portal, you can easily switch between all the tenants you have access to, enabling
streamlined oversight without compromising clarity or security.
1. Log into the [MATTR Portal](https://portal.mattr.global).
2. Select the **Create/switch tenant** button on the top-right side of the screen.\
The *All tenants* panel is displayed, listing any existing tenants.
3. Select the **Create new** button.\
The *New tenant* form is displayed.
4. Use the *Region* dropdown list to select the region your tenant will be hosted in.
5. Use the *Tenant subdomain* text box to insert a subdomain for your tenant (e.g. `my-first-tenant`).
6. Use the *Tenant name* text box to insert a meaningful and friendly name for your tenant (e.g. *My first tenant*).
7. Select the **Create** button to create the new tenant.
8. Copy the displayed tenant information (`audience`, `auth_url`, `tenant_url`, `client_id` and `client_secret`) which is required for the next step.
The `client_secret` is only displayed immediately after the client is created.
Once you navigate away from this screen, the client secret will be masked and
cannot be retrieved again. Ensure you save it securely at this point.
### Interacting with the tenant [#interacting-with-the-tenant]
You can interact with your tenant and use MATTR VII capabilities either using the
[MATTR Portal](/docs/platform-management/portal) or via direct API calls. The
Portal provides a user-friendly interface for managing
your tenant, while the APIs allow for programmatic access to MATTR VII capabilities.
You can use the Portal's various [features and functionalities](/docs/platform-management/portal) to
interact with your tenant and use MATTR VII capabilities:
* Every action in the portal is within the context of a specific tenant. This means that
all configurations, settings and data you manage are associated with the selected tenant.
* Use the drop-down list in the top-left corner to switch between tenants.
* Use the navigation panel on the left-hand side to access different functionalities.
**Capabilities**
The Portal currently supports the following functionalities:
* **Platform management**: Manage your MATTR VII tenants:
* Tenant management: Create, view and delete tenants. This capability is based on the Management APIs.
* Users, clients & roles: Manage users and clients for your tenants. These capabilities are
based on the [Clients](/docs/api-reference/management/clients/createTenantClient) and
[Members](/docs/api-reference/management/members/inviteTenantMember) endpoints in the Management API.
* [Custom domain](/docs/platform-management/custom-domain-guide): Configure a
[Custom domain](/docs/platform-management/custom-domain-overview) for the selected tenant. This capability is
based on
[configuring a Custom domain](/docs/platform-management/custom-domain-guide#create-custom-domain-configuration)
using an [API request](/docs/platform-management/custom-domain-api-reference#create-a-custom-domain)
and [verifying the Custom domain](/docs/platform-management/custom-domain-guide#verify-custom-domain) using
an [API request](/docs/platform-management/custom-domain-api-reference#verify-custom-domain).
* Monitoring: Query and inspect analytic events in your environment. This capability is based on
the [Analytic APIs](/docs/platform-management/analytics/overview).
* Webhooks: Create a [Webhook](/docs/platform-management/webhooks-overview) to subscribe to events.
Available options are similar to those described for [creating a Webhook](/docs/platform-management/webhooks-guide)
using an [API request](/docs/platform-management/webhooks-api-reference#create-a-webhook).
* DIDs: View Decentralized Identifiers (DIDs) available on your tenant. This includes any
[did:key](/docs/concepts/dids#didkey) and/or [did:web](/docs/concepts/dids#didweb) available on your
tenant. Note that you cannot use the SSP to create DIDs.
* Certificates: Manage [IACAs](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca),
[DTS CA](/docs/digital-trust-service/certificates-overview) and
[Verifier root CA](/docs/verification/certificates/overview) certificates on your tenant.
* **Digital Trust Service**: Manage your DTS:
* Create and manage
[participants](/docs/digital-trust-service/vical-guide#create-participants-and-add-issuer-certificates).
* Create and manage
[credential types](/docs/digital-trust-service/vical-guide#create-participants-and-add-issuer-certificates).
* Publish the DTS' [policy](/docs/digital-trust-service/vical-guide#manually-publish-a-vical) and
control auto-publishing capabilities.
* **Credential issuance**: Manage [OID4VCI](/docs/issuance/oid4vci-overview) workflow components and
configuration:
* Authentication provider: Configure and edit an authentication provider to be used during
credential issuance flows. Available options are similar to those described for
[configuring an Authentication provider](/docs/issuance/authorization-code/authentication-provider/guide) using an
[API request](/docs/issuance/authorization-code/authentication-provider/api-reference#configure-an-authentication-provider).
* Interaction hook: Configure an interaction hook to redirect a user to a custom component
during the credential issuance journey. Available options are similar to those described for
[configuring an Interaction hook](/docs/issuance/authorization-code/interaction-hook/tutorial) using an
[API request](/docs/issuance/authorization-code/interaction-hook/api-reference#create-an-interaction-hook).
* Claims sources: Configure and edit claims sources to fetch claims from an external endpoint
and use them when issuing credentials. Available options are similar to those described for
[configuring a Claims source](/docs/issuance/claims-source/overview) using an
[API request](/docs/issuance/claims-source/api-reference#configure-a-claims-source).
* Credential configurations: Create mDocs, CWT and Semantic CWT credential
configurations. Available options are similar to those described for
[creating a Credential configuration](/docs/issuance/credential-configuration/overview) using an API
request.
* Credential offer: Create a credential offer by specifying credential configurations and
request parameters. This capability is based on [creating a Credential offer](/docs/issuance/credential-offer/guide)
using an
[API request](/docs/issuance/authorization-code/api-reference#create-credential-offer), with
some additional capabilities to share the offer with the intended holder.
* **Credential verification**: Configure
[mDocs online verification workflows](/docs/verification/remote-web-verifiers/workflow):
* Trusted issuers: Configure and manage mDocs issuers that can be trusted when verifying mDocs
presented online. Available options are similar to those described for
[creating a trusted issuer](/docs/verification/remote-verification-api-reference/trusted-issuers#create-a-trusted-issuer)
using an API request.
* Supported wallets: Configure and manage digital wallet applications that can present mDocs
online for verification, and how to interact with these wallets. Available options are similar
to those described for
[creating a wallet provider](/docs/verification/remote-verification-api-reference/wallet-providers#create-a-wallet-provider)
using an API request.
* Applications: Configure and manage applications that can create mDocs online verification
sessions, and how to interact with these applications. Available options are similar to those
described for
[creating a verifier application](/docs/verification/remote-verification-api-reference/verifier-applications#create-a-verifier-application)
using an API request.
**Roles and permissions**
The Portal is designed to be used by different roles within an organization. The MATTR Portal UI is aligned with
the user's role and the permissions assigned to them. This means that users will only see the
features and functionalities that are relevant to their role.
* When you create a tenant, you are automatically assigned an `admin` role for that tenant. This
role grants you full access to manage all aspects of the tenant.
* If you are invited to manage a tenant created by someone else, your assigned role may differ. This
means you might have access to a limited set of features based on your permissions.
For more details on on available roles
and associated permissions, refer to the
[Access Control documentation](/docs/platform-management/access-control).
Perform the following steps to interact with your MATTR VII tenant via APIs:
**Choose an endpoint**
Select a MATTR VII endpoint to make a request to. The following resources might be helpful:
* The [API Reference](/docs/api-reference) offers an exhaustive list of all available endpoints and their
request structures in different languages.
* Different tutorials and guides can be used to learn what endpoints are required for specific
capabilities and workflows.
* Refer to the [Access control](/docs/platform-management/access-control) section to learn more
about what endpoints your client will be able to access.
We recommend using the MATTR VII [Postman
collection](/docs/api-reference#postman-collection)
to make requests to your MATTR VII tenant. While this isn't an explicit
prerequisite it can really speed things up.
**Obtain an access token**
Most of the MATTR VII endpoints are protected and require providing a bearer access token when
making a request. If you are making a request to an unprotected endpoint (as detailed in the
[API Reference](/docs/api-reference)), you do not need to obtain an access token and can continue to the
next step.
Use your [access credentials](/docs/platform-management/portal#getting-started) and make a request of the
following structure to obtain an access token:
```http title="Request"
POST https://{auth_server}/oauth/token
```
* `auth_server` : Replace with the `auth_url` value obtained when you created your tenant.
```json title="Request Body"
{
"client_id": "F5qae****************************",
"client_secret": "Wzc8J**********************************************************",
"audience": "learn.vii.au01.mattr.global",
"grant_type": "client_credentials"
}
```
* `client_id` : Replace with the `client_id` value obtained when you created your tenant.
* `client_secret` : Replace with the `client_secret` value obtained when you created your tenant.
* `audience` : Replace with the `audience` value obtained when you created your tenant.
* `grant_type` : Always use `client_credentials` as a static value, regardless of your specific
[login credentials](/docs/platform-management/portal#getting-started).
*Response*
```json title="Response body"
{
"access_token": "eyJhb********************************************************************", // [!code focus]
"expires_in": 14400,
"token_type": "Bearer"
}
```
* The returned `access_token` will enable access to endpoints as per the role assigned to the
client. Refer to [Access control](/docs/platform-management/access-control) for more Information.
* You will need to obtain a new access token whenever it expires. Our
[Postman collection](/docs/api-reference#postman-collection)
includes a pre-request script that obtains an access token when it is missing or has expired.
**Construct the request**
Construct an API request using the selected endpoint path and the `tenant_url` value obtained when you created your tenant:
```http title="Request template"
{method} https://{tenant_url}/{path}
```
For example, a request to
[retrieve all IACAs](/docs/issuance/certificates/api-reference/iaca#retrieve-all-iacas) from a
tenant whose `tenant_url` is `learn.vii.au01.mattr.global` should be constructed as follows:
```http title="Request example"
GET https://learn.vii.au01.mattr.global/v2/credentials/mobile/iacas
```
If the operation has a request body you should structure it too, based on the details provided in
the [API Reference](/docs/api-reference) or relevant tutorial.
Whatever tool or language your are using to make the request, make sure you
include the `access_token` in the request header when making requests to
protected endpoints. Refer to the [API Reference](/docs/api-reference) for
request samples.
**Handle the response**
The endpoint would respond with a standard
[HTTP status code](https://www.rfc-editor.org/rfc/rfc9110.html#name-status-codes) and a response
body. These differ between endpoints and are detailed in the [API Reference](/docs/api-reference).
You can now adjust your implementation to handle these responses to achieve the desired outcome.
### Inviting users [#inviting-users]
To support collaboration, you can invite other users to access the MATTR Portal and manage tenants that you administer.
When you invite a user to manage a tenant, you select the role they will hold within that tenant. This role defines what they can view, modify, or manage—ensuring fine-grained access control that aligns with your trust and governance requirements.
Perform the following steps to invite a user to interact with a tenant you administer in the MATTR Portal:
1. Open **Platform Management** in the left navigation panel and select **Tenant**.\
The tenant management screen appears.
2. Click **Create/Switch tenant** at the top-right.\
The All Tenants window opens.
3. Click **Switch** next to the desired tenant.
4. Under **Platform Management**, select **Users, clients & roles**.
5. Go to the **Users** tab and click **Invite**.
6. Enter the user's email in the *Email* field.
7. Select roles for the user using the checkboxes in the *Tenant access* section.
8. Click **Invite** to send the invitation.
* If the invited user already has access to the Portal, they will immediately see the new tenant in their list of accessible tenants. When they select this tenant, their Portal UI would be updated to reflect the permissions associated with their role in that tenant.
* If the invited user does not have access to the Portal yet, they’ll receive an email to accept the invite and log into the Portal. Invites expire after 5 days.
By default, invited users are assigned to the **Contributor** plan. As Contributors, they can perform all actions permitted by their assigned role within the specific tenant they were invited to. However, they cannot create new tenants or switch to other tenants unless they are explicitly invited to those tenants and/or are given the necessary permissions.
Inviting users is only supported for Portal users that are members of the tenant. Machine-to-machine (M2M) clients cannot invite users. Calling the [invite a tenant member](/docs/api-reference/management/members/inviteTenantMember) endpoint with an M2M client token returns a `404 Resource Not Found` response, even when the client holds an `admin` role.
If you provision a tenant programmatically using an M2M client, that client is not a Portal member of the tenant and therefore cannot invite users. To manage tenant membership, create the tenant from the Portal as the steps above describe, so the creating Portal user becomes a member automatically. You can then invite additional users from the Portal UI or by calling the invitation endpoint with that Portal user's access token.
### Being invited to a tenant [#being-invited-to-a-tenant]
Another user with sufficient privileges can invite you to manage a tenant they control. In this case:
* You are assigned a specific role as part of the invitation.
* Your permissions in that tenant are determined by the role you’re given—limiting or enabling specific actions according to that scope.
* You can work across multiple tenants, each with different roles depending on how you've been invited.
## Single Sign-On (SSO) [#single-sign-on-sso]
The MATTR Portal supports Single Sign-On (SSO) as a paid add-on, allowing your team to authenticate
using your organization's existing identity provider (IdP).
[Contact us](mailto:dev-support@mattr.global) to learn more and enable SSO for your organization.
### Supported identity providers [#supported-identity-providers]
MATTR supports any identity provider that integrates with Auth0 as an enterprise connection. This
includes providers such as Microsoft Entra ID, Google Workspace, Okta, ADFS, and SAML-based IdPs.
Refer to the
[Auth0 Enterprise Identity Providers](https://auth0.com/docs/authenticate/identity-providers/enterprise-identity-providers/enable-enterprise-connections)
documentation for a full list of supported providers.
### Enabling SSO [#enabling-sso]
To enable SSO for your organization, provide the following to the MATTR team:
* **Email domain**: The email domain used by your organization (e.g. `your-company.com`).
* **Logo**: A logo to display on the Portal login page for your organization.
Once received, the MATTR team will configure SSO for your domain and onboard your first user.
### Managing SSO users [#managing-sso-users]
After SSO is enabled, your first user is onboarded by the MATTR team. From there, you can
[invite additional users](#inviting-users) to specific tenants, and they will be able to log into the
Portal via SSO.
Enabling SSO does not automatically grant all users in your organization access
to the Portal. Each user must be explicitly
[invited to a tenant](#inviting-users) before they can log in. This ensures
that access to tenants and their associated data remains tightly controlled.
## What's next? [#whats-next]
We recommend starting with one of the following resources based on your needs:
* For issuers:
* [OID4VCI Authorization Code tutorial](/docs/issuance/authorization-code/tutorial): Learn how to configure an OID4VCI Authorization Code flow.
* [OID4VCI Pre-authorized Code tutorial](/docs/issuance/pre-authorized-code/tutorial): Learn how to configure an OID4VCI Pre-authorized Code flow.
* [Revocation tutorial](/docs/issuance/revocation/tutorial): Learn how to issue revocable credentials and manage their revocation status.
* For verifiers:
* [Remote web verification tutorial](/docs/verification/remote-web-verifiers/tutorial): Learn how to build a web application that can verify mDocs remotely.
* [Remote mobile verification tutorial](/docs/verification/remote-mobile-verifiers/tutorial): Learn how to build a mobile application that can verify mDocs remotely.
* General:
* [API reference](/docs/api-reference): Explore the API reference documentation to understand the available endpoints, request/response formats, and authentication methods.
* [Postman collection](/docs/api-reference#postman-collection): Download the Postman collection to quickly test and interact with the APIs. The collection includes pre-configured requests for common operations, making it easier to get started with API development.
# Roles and Permissions
URL: /docs/platform-management/roles-and-permissions
This page lists the MATTR VII platform [roles](/docs/platform-management/access-control) and the full set of endpoints each role is authorized to access.
# API Reference
URL: /docs/platform-management/webhooks-api-reference
## Create a Webhook [#create-a-webhook]
## Retrieve all Webhooks [#retrieve-all-webhooks]
## Retrieve a Webhook [#retrieve-a-webhook]
## Update a Webhook [#update-a-webhook]
## Delete a Webhook [#delete-a-webhook]
## Retrieve Webhook JWKs [#retrieve-webhook-jwks]
# How to create and use a Webhook
URL: /docs/platform-management/webhooks-guide
MATTR VII [Webhooks](/docs/platform-management/webhooks-overview) enable retrieving information that is
generated during an API interaction but is not included in the request or response payloads.
## Prerequisites [#prerequisites]
* A configured MATTR VII workflow that utilizes a supported Webhook event type:
* [OID4VCI workflow](/docs/issuance/oid4vci-overview).
## Overview [#overview]
Creating and using a Webhook hook comprises the following steps:
1. [Create a MATTR VII Webhook](#create-a-mattr-vii-webhook).
2. [Verify the Webhook request](#verify-webhook-requests).
3. [Use the Webhook payload](#use-webhook-events-payload).
### Create a MATTR VII Webhook [#create-a-mattr-vii-webhook]
This step can be performed either via the MATTR Portal or via the MATTR VII APIs.
1. Log in to the [MATTR Portal](/docs/platform-management/portal) and navigate to the **Webhooks**
section under **Platform Management**.
2. Click on **Create new**.
3. Use the *Event* type checkbox to select **Open ID credential issued**.
4. In the *URL* field, enter the HTTPS endpoint where you want to receive the Webhook events.
5. Expand the *Verify request* panel and copy the *Webhook ID* value. You will
need it to verify the Webhook requests in the next step.
Make a request of the following structure to
[create a Webhook](/docs/platform-management/webhooks-api-reference#create-a-webhook):
```http title="Request"
POST /v1/webhooks
```
```json title="Request body"
{
"events": ["OpenIdCredentialIssued"],
"url": "https://example.com"
}
```
* `events` : This array includes the event types that will trigger this Webhook. Currently allowed
types:
* `OpenIdCredentialIssued` : Triggered upon completion of an
[OID4VCI workflow](/docs/issuance/oid4vci-overview).
* `OpenIdCredentialIssuedSummary` : Triggered upon completion of an
[OID4VCI workflow](/docs/issuance/oid4vci-overview) but only provides a summary of the issuance
event, leaving out the credential object.
* `url` : This is the URL that will receive the Webhook events data payload when they are triggered
by MATTR VII for the specified events:
* Must be a valid URL.
* Must use the HTTPS protocol.
* Must not be an IP address.
* Must not include query parameters or fragments.
* Non-ASCII characters are normalized.
* Must return a 2xx response, otherwise it will go through a retry cycle and eventually fail.
*Response*
```json title="Response body"
{
"id": "0c099611-19c4-4f29-8724-6b9e5ba1ef7c", // [!code focus]
"events": ["OpenIdCredentialIssued"],
"url": "https://example.com",
"disabled": false // [!code focus]
}
```
* `id` : Unique identifier for the created Webhook. You will need it to
[verify Webhook events](#verify-webhook-requests).
* `disabled` : Set to `false` by default, indicating the Webhook is active. When set to `true` the
Webhook is disabled and no events are triggered and sent to the defined `url`. You can disable an
active Webhook by
[making an update request](/docs/platform-management/webhooks-api-reference#update-a-webhook).
### Verify Webhook requests [#verify-webhook-requests]
Once the Webhook is configured, MATTR VII will make a POST request to the specified `url` each time
an event is triggered. The request will include a payload with the event data. To ensure the
integrity and authenticity of the request, it is important to verify that the request is indeed from
MATTR VII and not from a malicious actor. To do this, you can use the following methods:
1. Compare the Webhook ID (`id`) that is obtained when
[creating a MATTR VII Webhook](#create-a-mattr-vii-webhook) with the `webhookId` specified in the
[event payload](#use-webhook-events-payload).
2. Verify the request HTTP signature.
**Verifying HTTP signatures**
All MATTR VII Webhook requests are signed using
[HTTP Message Signatures](https://datatracker.ietf.org/doc/draft-ietf-httpbis-message-signatures/)
(an IETF draft standard). You can retrieve the public keys MATTR VII uses to sign the HTTP and use
them to verify the HTTP signature.
Make the following request to
[retrieve the public keys](/docs/platform-management/webhooks-api-reference#retrieve-webhook-jwks):
```http title="Request"
GET /v1/webhooks/jwks
```
This endpoint is publicly available by design and does not require authentication.
*Response*
```json title="Response body"
{
"keys": [
{
"kty": "OKP",
"crv": "Ed25519",
"kid": "1608085995",
"x": "1NYsB58B9bNmReXqyQR8R_DeJtoLHSW-JsyZVmV2EWQ"
}
]
}
```
* `kid` : Use this key to identify which key a particular HTTP request is signed with.
You can cache the response as these public keys are not expected to change often.
**Verification implementations**
* To facilitate verification of MATTR VII Webhook request, we provide a
[typescript based library](https://www.npmjs.com/package/@mattrglobal/http-signatures) that can be
used for verification or serve as a reference implementation to develop a verification SDK in
another programming language.
* To learn more about verifying our Webhook requests, have a look at the
[Open Source MATTR Http-Signatures library](https://github.com/mattrglobal/http-signatures).
### Use Webhook events payload [#use-webhook-events-payload]
The event payload has a common structure in JSON format:
```json title="Event payload structure"
{
"deliveryId": "9a8b0b72-5c4f-4925-b101-528edbb5d75d",
"deliveryTimestamp": "2022-08-30T01:26:38.325Z",
"webhookId": "5be4f663-af93-41d8-8542-f5c9a2a2d588",
"event": {
"id": "de347800-d56f-4262-9f19-52b34856a933",
"type": "OpenIdCredentialIssued",
"payload": "{event payload}",
"timestamp": "2022-08-30T01:26:38.308Z"
}
}
```
* `deliveryId` : The unique identifier assigned each time the event is delivered to a Webhook
endpoint.
* `deliveryTimestamp` : The time when the event was delivered.
* `webhookId` : The unique Webhook identifier returned in the [response](#response) during its
creation.
* `event` : Includes the following event details:
* `id` : Unique event identifier.
* `type` : Either `OpenIdCredentialIssued` or `OpenIdCredentialIssuedSummary`.
* `payload` : Contains the specific event data. Refer to [Event payloads](#event-payload) below
for examples.
* `timestamp` : Specifies the date/time when the event was created.
When a Webhook event is sent from MATTR VII, `x-mattr-webhook-id` and `x-mattr-event-type`
are added as request headers.
MATTR VII does not guarantee the delivery of events in the exact order that they are
generated or that no duplicate events will be received by the Webhook endpoint. You can
safeguard against duplicates by checking the `event.id` that is provided in the event
payload, this is a unique identifier for each event generated by MATTR VII. Alternatively,
make your event processing idempotent.
#### Timeout and retry [#timeout-and-retry]
When a Webhook fails to send data to the configured `url`, or when a session timeout occurs, a
maximum of three retry attempts are made at set intervals. The interval time increases exponentially
according to the number of retries that have been attempted. If the Webhook fails to send following
the retry attempts, it will be marked as failed.
By default, the Webhook has a response timeout of three seconds. If the server receiving the Webhook
event does not respond within this period it is considered a failure.
#### Event payload [#event-payload]
Below are examples of event payloads:
*mDocs*
```json title="mDocs event payload example"
{
"format": "mso_mdoc",
"userId": "7382276d-ef75-4d17-8fb0-1d3aec4647ab",
"credentialProfile": "mobile",
"credentialConfigurationId": "1d8c7ad7-84ce-4519-8365-7af986e4ee0e",
"credentialOfferId": "3b4f5cf6-4069-4c51-bbed-8165b4f9a889",
"credentialId": "9613ac5e-a0ba-4512-ba0b-90e91b2744bc",
"userClaims": {
"Name": "John"
},
"credential": "{base64url_encoded_credential}"
}
```
* `format` : Issued credential format (For mDocs this would always be `mso_mdoc`).
* `userId` Unique identifier of the user the credential was issued to on the MATTR VII tenant.
* `credentialProfile` : Issued credential profile (For mDocs this would always be `mobile`).
* `credentialOfferId` : Unique identifier of the
[Credential Offer](/docs/issuance/credential-offer/overview) that was used to initiate the issuance
flow. This optional field is only included for credentials issued via the [Pre-authorized Code flow](/docs/issuance/pre-authorized-code/overview).
* `credentialConfigurationId` : Unique identifier of the
[Credential configuration](/docs/issuance/credential-configuration/overview) that was used to issue
the credential.
* `credentialId` : Unique identifier of the issued credential on the MATTR VII tenant.
* `userClaims` : Claims that were collected for this user as part of the issuance workflow and
persisted on the MATTR VII tenant.
* `credential` : Base64 encoded byte\[] of the issued mDoc.
*CWT credentials*
```json title="CWT credentials event payload example"
{
"format": "cwt",
"userId": "7382276d-ef75-4d17-8fb0-1d3aec4647ab",
"credentialProfile": "compact",
"credentialConfigurationId": "1d8c7ad7-84ce-4519-8365-7af986e4ee0e",
"credentialId": "9613ac5e-a0ba-4512-ba0b-90e91b2744bc",
"userClaims": {
"Name": "John"
},
"credential": "{base64url_encoded_credential}"
}
```
* `format` : Issued credential format (For CWT Credentials this would always be `cwt`).
* `userId` Unique identifier of the user the credential was issued to on the MATTR VII tenant.
* `credentialProfile` : Issued credential profile (For CWT credentials this would always be
`compact`).
* `credentialConfigurationId` : Unique identifier of the
[Credential configuration](/docs/issuance/credential-configuration/overview) that was used to issue
the credential.
* `credentialId` : Unique identifier of the issued credential on the MATTR VII tenant.
* `userClaims` : Claims that were collected for this user as part of the issuance workflow and
persisted on the MATTR VII tenant.
* `credential` : Base64 encoded byte\[] of the issued CWT credential.
```json title="mDocs event payload example"
{
"format": "mso_mdoc",
"userId": "7382276d-ef75-4d17-8fb0-1d3aec4647ab",
"credentialProfile": "mobile",
"credentialConfigurationId": "1d8c7ad7-84ce-4519-8365-7af986e4ee0e",
"credentialOfferId": "3b4f5cf6-4069-4c51-bbed-8165b4f9a889",
"credentialId": "9613ac5e-a0ba-4512-ba0b-90e91b2744bc",
"userClaims": {
"Name": "John"
}
}
```
* `format` : Issued credential format:
* `cwt` : CWT credentials.
* `mso_mdoc` : mDocs.
* `userId` Unique identifier of the user the credential was issued to on the MATTR VII tenant.
* `credentialProfile` : Issued credential profile:
* `compact` : CWT credentials.
* `mobile` : mDocs.
* `credentialConfigurationId` : Unique identifier of the
[Credential configuration](/docs/issuance/credential-configuration/overview) that was used to issue
the credential.
* `credentialOfferId` : Unique identifier of the
[Credential Offer](/docs/issuance/credential-offer/overview) that was used to initiate the issuance
flow. This optional field is only included for credentials issued via the [Pre-authorized Code flow](/docs/issuance/pre-authorized-code/overview).
* `credentialId` : Unique identifier of the issued credential on the MATTR VII tenant.
* `userClaims` : Claims that were collected for this user as part of the issuance workflow and
persisted on the MATTR VII tenant.
MATTR VII does not guarantee the delivery of events in the exact order that they are generated or that no duplicate events will be received by the Webhook endpoint. You can safeguard against duplicates by checking the `deliveryId`. Alternatively, make your event processing idempotent.
# Webhooks
URL: /docs/platform-management/webhooks-overview
## Overview [#overview]
MATTR VII Webhooks enable retrieving information that is generated during an API interaction but is
not included in the request or response payloads.
You can subscribe to specific events that are triggered on set MATTR VII operations to retrieve the
required information whenever it is generated. When the subscribed event is triggered, the
information relating to that event is published via the Webhook through to the URL(s) configured on
the subscription.
The following MATTR VII event types can be subscribed to:
* `OpenIdCredentialIssued` : Triggered upon completion of an
[OID4VCI workflow](/docs/issuance/oid4vci-overview).
* `OpenIdCredentialIssuedSummary` : Triggered upon completion of an
[OID4VCI workflow](/docs/issuance/oid4vci-overview) but only provides a summary of the issuance event,
leaving out the `credential` object.
One possible use-case for these event payloads is to provide the DID that was used as the
`credentialSubject.id` when binding and issuing the credential to the subject’s wallet.
MATTR VII does not guarantee the delivery of events in the exact order that they are generated
or that no duplicate events will be received by the Webhook endpoint. You can safeguard against
duplicates by checking the event.id that is provided in the event payload, this is a unique
identifier for each event generated by MATTR VII. Alternatively, make your event processing
idempotent.
## Webhook validation [#webhook-validation]
To validate the integrity and authorship of Webhooks generated by the MATTR VII platform, all
Webhook events are signed using
[HTTP Message Signatures](https://datatracker.ietf.org/doc/draft-ietf-httpbis-message-signatures/)
(an IETF draft standard).
MATTR strongly encourages verifying each Webhook event to provide a suitable level of protection to
integrations consuming the event information:
* Verify the HTTP signatures.
* Compare the `webhookId` identifier that is generated when the Webhook is created to the
`webhookId` specified in the request.
### Verifying HTTP signatures [#verifying-http-signatures]
You can [obtain](/docs/platform-management/webhooks-api-reference#retrieve-webhook-jwks) the public keys MATTR
VII uses to sign the HTTP and use them to verify the HTTP signature. The response key set is
relatively static and only expected to change on rare occasions, such as when performing key
rotation.
To facilitate verification of MATTR VII Webhook requests, we provide a
[typescript-based library](https://www.npmjs.com/package/@mattrglobal/http-signatures) that can be
used for verification or serve as a reference implementation to develop a verification SDK in
another programming language.
To learn more about verifying our Webhook requests, take a look at the
[Open Source MATTR Http-Signatures library](https://github.com/mattrglobal/http-signatures).
## Timeouts and retry [#timeouts-and-retry]
When a Webhook fails to send data to the configured URL, or when a session timeout occurs, a maximum
of three retry attempts are made at set intervals. The interval time increases exponentially
according to the number of retries that have been attempted. If the webhook fails to send following
the retry attempts, it will be marked as failed.
By default, the Webhook has a response timeout of three seconds. If the server receiving the Webhook
event does not respond within this period it is considered a failure.
# Learn how to consume MATTR VII Webhooks
URL: /docs/platform-management/webhooks-tutorial
## Overview [#overview]
In an [OID4VCI issuance workflow](/docs/issuance/oid4vci-overview), you may want to trigger additional actions
once a credential has been successfully issued. For example, sending a notification, updating a database, or invoking a downstream system.
[MATTR VII Webhooks](/docs/platform-management/webhooks-overview) provide a simple way to achieve this by
notifying consuming applications when specific events occur.
This tutorial will guide you through creating a webhook in your MATTR VII tenant and using a
sample application to consume the webhook, verify the request, and display the event payload when a
credential is issued. You can then adapt principles demonstrated in this tutorial to suit your
specific use cases.
## Tutorial overview [#tutorial-overview]
The current tutorial builds upon the [Authorization Code](/docs/issuance/authorization-code/tutorial) or [Pre-authorized Code](/docs/issuance/pre-authorized-code/tutorial) tutorials by adding the following steps:
1. **Create a MATTR VII Webhook:** Configure MATTR VII to send a Webhook event when a credential is
issued.
2. **Set up a webhook receiver sample app:** Use a sample Node.js application that achieves the following:
* Exposes a public endpoint to receive Webhook events.
* Verifies the authenticity of incoming requests using MATTR VII's public keys.
* Logs the received event payload to the console.
## Prerequisites [#prerequisites]
* Complete either the [Authorization Code](/docs/issuance/authorization-code/tutorial) or [Pre-authorized Code](/docs/issuance/pre-authorized-code/tutorial) tutorials to configure a credential issuance flow.
* Node.js 18+ installed locally.
* To test locally you will need to expose your local consumer app to the internet. You can do this by setting up a [free ngrok account](https://ngrok.com/), using a Cloudflare tunnel or using your own solution. For this tutorial we will be using ngrok. Sign up for a free account at [ngrok.com](https://ngrok.com/).
## Tutorial steps [#tutorial-steps]
### Create a MATTR VII Webhook [#create-a-mattr-vii-webhook]
1. Log in to the [MATTR Portal](/docs/platform-management/portal) and navigate to the **Webhooks**
section under **Platform Management**.
2. Click on **Create new**.
3. Use the *Event* type checkbox to select **Open ID credential issued**. This event is triggered upon
completion of an [OID4VCI workflow](/docs/issuance/oid4vci-overview).
4. In the *URL* field, enter `https://example.com`. This is the URL that will receive the Webhook events data payload when they are triggered by MATTR VII for the specified events. We will update this to your webhook receiver sample app URL later.
5. Click on **Create** to create the Webhook.
6. Copy the *ID* value. You will need it to configure the webhook receiver sample app.
Make a request of the following structure to
[create a Webhook](/docs/platform-management/webhooks-api-reference#create-a-webhook):
```http title="Request"
POST /v1/webhooks
```
```json title="Request body"
{
"events": ["OpenIdCredentialIssued"],
"url": "https://example.com"
}
```
* `events` : This array includes the event types that will trigger this Webhook. The `OpenIdCredentialIssued` event is triggered upon completion of an [OID4VCI workflow](/docs/issuance/oid4vci-overview).
* `url` : This is the URL that will receive the Webhook events data payload when they are triggered by MATTR VII for the specified events. We will update this to your actual webhook consumer URL later.
*Response*
```json title="Response body"
{
"id": "0c099611-19c4-4f29-8724-6b9e5ba1ef7c", // [!code focus]
"events": ["OpenIdCredentialIssued"],
"url": "https://example.com",
"disabled": false //
}
```
* `id` : Unique identifier for the created Webhook. You will need it to configure the webhook receiver sample app.
### Set up the Webhook receiver sample app [#set-up-the-webhook-receiver-sample-app]
1. Clone the [MATTR Sample Apps repo](https://github.com/mattrglobal/sample-apps) to your machine and navigate to the `webhook-app` folder.
2. Rename the `env-example` file to `.env`.
3. Open the `.env` file and set the following environment variables:
* `MATTR_TENANT_URL` : Your MATTR VII tenant URL.
* `MATTR_WEBHOOK_ID` : The Webhook ID you copied when creating the Webhook in the previous step.
* `NGROK_AUTHTOKEN` : Your ngrok authentication token.
* `PORT` : Port for the webhook server (optional, defaults to 3000).
4. Install and start the app:
```bash
npm install
npm run dev
```
Or using Docker:
```bash
docker compose up --build
```
You should see output indicating that the server is running and ngrok is set up, as shown in the following image:
5. Copy the `Public Webhook URL` from the ngrok output (e.g., `https://5d81c4374916.ngrok-free.app/webhook`). You will use it to update the Webhook URL in MATTR VII.
The sample app core logic is within the `/src/index.js` file. You can review annotations in the sample app code to understand how it:
* Fetches MATTR VII's public keys to verify incoming requests.
* Verifies the request signature.
* Logs the received event payload to the console.
### Update the Webhook URL in MATTR VII [#update-the-webhook-url-in-mattr-vii]
1. Log in to the [MATTR Portal](/docs/platform-management/portal) and navigate to the **Webhooks**
section under **Platform Management**.
2. Select the Webhook you created earlier to open its details.
3. In the *URL* field, enter the `Public Webhook URL` you copied from the ngrok output in the previous step.
4. Click on **Save** to apply changes.
Make a request of the following structure to
[create a Webhook](/docs/platform-management/webhooks-api-reference#create-a-webhook):
```http title="Request"
PUT /v1/webhooks/{id}
```
* `id` : The unique identifier of the Webhook you created earlier.
```json title="Request body"
{
"events": ["OpenIdCredentialIssued"],
"url": "https://example.com"
}
```
* `url` : Enter the `Public Webhook URL` you copied from the ngrok output in the previous step.
### Test the workflow [#test-the-workflow]
1. Use the MATTR GO example app to claim the credential offer you created in the [Authorization Code](/docs/issuance/authorization-code/tutorial) or [Pre-authorized Code](/docs/issuance/pre-authorized-code/tutorial) tutorials.
2. When the credential is issued, MATTR VII will trigger the webhook.
3. You should see the received event payload in the terminal window where your sample app webhook receiver is running. You can refer to the [Webhooks guide](/docs/platform-management/webhooks-guide#event-payload) for a detailed structure of the events payload.
## What’s next? [#whats-next]
Now that you have a working webhook consumer, you can extend the application to perform more meaningful actions based on the received events, as opposed to merely logging them. For example, you could:
* Store credential issuance events in a database.
* Trigger downstream APIs for automated workflows.
### Production considerations [#production-considerations]
When moving your Webhooks usage into production, consider the following best practices:
* **Secure your Webhook endpoint:**
* Require HTTPS at all times.
* Use a firewall or allowlist to limit inbound traffic.
* Validate that requests are signed with MATTR VII’s public keys (as shown in this tutorial).
* **Make event processing idempotent:**
* MATTR VII does not guarantee that events arrive in order or only once.
* Deduplicate by checking the `event.id` or `deliveryId` in the payload before processing.
* Ensure that reprocessing the same event has no negative effect (e.g., sending duplicate
emails, double-updating a record).
* **Add monitoring and logging:**
* Capture request/response logs for debugging and auditing.
* Monitor webhook failures, retries, and timeouts.
* Consider alerting if repeated failures occur.
# Android app signing
URL: /docs/verification/android-app-signing
When embedding remote verification capabilities into a mobile application, it's important to ensure that only trusted applications can make verification requests to your MATTR VII verifier tenant.
For Android applications specifically, this is done by configuring the `packageSigningCertificateThumbprints` property when creating the [MATTR VII Verifier Application](/docs/verification/remote-verification-api-reference/verifier-applications#create-a-verifier-application).
Every Android app must be signed with a certificate before it can be installed. This signing certificate identifies the developer or organization responsible for the app and is used by Android to verify authenticity and integrity.
When integrating with MATTR verification capabilities, the package signing certificate thumbprint acts as a unique cryptographic identifier for your app's signing key.
By verifying this thumbprint, the Verifier can confirm that the incoming request originated from a trusted and unmodified app.
If the thumbprint doesn't match exactly, the Verifier will reject the request. This ensures that only trusted client applications — those signed with a matching certificate — can initiate verification requests.
The purpose of this page is to explain how to obtain the signing certificate thumbprint for your Android app.
## What is a Package Signing Certificate Thumbprint? [#what-is-a-package-signing-certificate-thumbprint]
A package signing certificate thumbprint is the hex-encoded SHA-256 hash of your app's signing certificate (the X.509 certificate bytes). It uniquely identifies the certificate used to sign your app and remains the same across app updates as long as the signing certificate does not change.
## Obtaining the Thumbprint [#obtaining-the-thumbprint]
### Production builds [#production-builds]
When your app is ready for release, the thumbprint you configure must match the signing certificate used for your production build.
Obtaining the thumbprint differs based on how you manage your signing keys - via Google Play App Signing or manually.
#### Google Play App Signing [#google-play-app-signing]
If your app uses Play App Signing, Google manages your app's signing key. You can find the SHA-256 fingerprint in the Google Play Console:
1. Go to **Google Play Console** > **Setup** > **App Signing**.
2. Locate the SHA-256 fingerprint under **App Signing Key Certificate**.
3. Copy the SHA-256 value.
4. Remove all `:` characters and convert the string to lowercase.
```js title="Example conversion"
const fingerprint = '91:F7:CB:F9:D6:81:53:1B:C7:A5:8F:B8:33:CC:A1:4D:AB:ED:E5:09:C5:12:34:56:78:9A:BC:DE:F0:12:34:56:78';
// Remove colons and convert to lowercase
const sha256Hex = fingerprint.replaceAll(":", "").toLowerCase();
console.log(sha256Hex)
```
5. Upload the processed value as your `packageSigningCertificateThumbprints` configuration.
For more information, refer to Google's documentation on [Play App Signing Overview](https://developer.android.com/studio/publish/app-signing) and [Manage App Signing Keys in Google Play Console](https://support.google.com/googleplay/android-developer/answer/9842756?hl=en).
#### Manual signing (CLI) [#manual-signing-cli]
1. Retrieve the SHA-256 fingerprint using the `keytool` command for the specific signing key alias:
```bash title="Command to get SHA-256 fingerprint"
keytool -list -v -keystore -alias
```
2. Copy the SHA-256 value.
3. Remove all `:` characters and convert the string to lowercase.
```js title="Example conversion"
const fingerprint = '91:F7:CB:F9:D6:81:53:1B:C7:A5:8F:B8:33:CC:A1:4D:AB:ED:E5:09:C5:12:34:56:78:9A:BC:DE:F0:12:34:56:78:9A:BC:DE:F0:12:34:56:78:9A:BC:DE:F0:12:34:56:78:9A:BC:DE:F0';
const sha256Hex = fingerprint.replaceAll(":", "").toLowerCase();
console.log(sha256Hex)
```
4. Upload the processed value as your `packageSigningCertificateThumbprints` configuration.
### Local builds (Debug) [#local-builds-debug]
1. Extract the signing certificate information directly from the `.apk` file using the apksigner tool:
```bash title="Command to get SHA-256 fingerprint from APK"
apksigner verify --print-certs path/to/your-debug-apk.apk
```
```nginx title="Example output"
Signer #1 certificate DN: C=US, O=Android, CN=Android Debug
Signer #1 certificate SHA-256 digest: f59105881315e61502274a499d6efc2d7cc71c5cae266e598290d36b59221f6d // [!code focus]
Signer #1 certificate SHA-1 digest: ca09773016ef4db66344ce0dac2827429ea875f1
Signer #1 certificate MD5 digest: c59905769e42c09530898c6dc413258f
```
2. Copy the SHA-256 digest and upload it as your `packageSigningCertificateThumbprints` configuration.
The default debug keystore is usually located at: `$HOME/.android/debug.keystore`.
For more details, refer to [Android Debug Signing](https://developer.android.com/studio/publish/app-signing#debug-mode).
## Best practices [#best-practices]
* If your app's signing key changes, you'll need to update your application configuration.
* Consider removing old thumbprints if you wish to **invalidate older app versions** — for example, after a key rotation or potential compromise.
* For development and testing, you can use the debug signing certificate thumbprint, but ensure to switch to the release signing certificate for production builds.
* Always keep your signing keys secure and avoid sharing them publicly.
# Choose your verification channel
URL: /docs/verification/choosing-a-channel
The first decision is where and how your users will present their credential. This page compares the
three channels so you can pick the one that fits your use case.
## Choose your verification channel [#choose-your-verification-channel]
The first decision is where and how your users will present their credential. Each channel has
different technical requirements, user experience characteristics, and standards underpinnings.
### In-person verification (ISO/IEC 18013-5) [#in-person-verification-isoiec-18013-5]
Best for: retail counters, border control, event entry, age verification at point of sale.
The holder presents their credential by displaying a QR code or initiating a BLE connection from
their wallet. Your verifier application reads the credential over a proximity channel, validates it
cryptographically, and returns the result, all within seconds.
* Uses Bluetooth Low Energy (BLE) for secure device-to-device communication.
* Supports offline verification.
* Aligned with [ISO/IEC 18013-5](https://www.iso.org/standard/69084.html).
**MATTR tooling:** [MATTR Pi Verifier SDKs](/docs/verification/sdks/overview) for iOS, Android, and
React Native.
See the [in-person verification overview](/docs/verification/in-person-overview) and
[quickstart](/docs/verification/in-person-quickstart) to get started.
### Remote web verification (ISO/IEC 18013-7 + OID4VP) [#remote-web-verification-isoiec-18013-7--oid4vp]
Best for: online account opening, e-commerce age gates, government service portals, insurance
applications.
Your web application initiates a verification request. The user's wallet receives the request (via
redirect or the Digital Credentials API), the user consents to share specific attributes, and the
verified result is returned to your backend.
* Supports same-device and cross-device flows.
* Uses [OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html) as the
presentation protocol.
* Aligned with [ISO/IEC 18013-7](https://www.iso.org/standard/91154.html).
**MATTR tooling:** [MATTR Pi Verifier Web SDK](/docs/verification/remote-web-verifiers/sdks/overview) +
[MATTR VII](/docs/digital-trust-service) as the backend verification service.
See the [remote web verification workflow](/docs/verification/remote-web-verifiers/workflow) and
[quickstart](/docs/verification/remote-web-verifiers/quickstart) to get started.
### Remote mobile verification (ISO/IEC 18013-7 + OID4VP) [#remote-mobile-verification-isoiec-18013-7--oid4vp]
Best for: native mobile apps for banking, ride-sharing, age-restricted delivery, telehealth.
Your native app triggers a verification request using the embedded Verifier SDK. The wallet app on
the same device (or a secondary device) responds with the credential presentation.
* Direct app-to-wallet communication on the same device.
* Supports cross-device scenarios where the wallet is on a different device.
* Uses [OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html) for
interoperable credential exchange.
**MATTR tooling:** [MATTR Pi Verifier Mobile SDK](/docs/verification/remote-mobile-verifiers/sdks/overview) +
[MATTR VII](/docs/digital-trust-service) as the backend verification service.
See the [remote mobile verification workflow](/docs/verification/remote-mobile-verifiers/workflow)
and [quickstart](/docs/verification/remote-mobile-verifiers/quickstart) to get started.
## Next steps [#next-steps]
Once you have chosen a channel, [decide what data you need to verify](/docs/verification/deciding-what-to-verify).
# Decide what data you need to verify
URL: /docs/verification/deciding-what-to-verify
mDoc verification supports selective disclosure, so you only request the specific data elements
relevant to your use case. This page covers common scenarios and how presentation requests work.
## Decide what data you need to verify [#decide-what-data-you-need-to-verify]
mDoc verification supports selective disclosure, meaning you only request the specific data elements
relevant to your use case. Common verification scenarios include:
| Use case | Typical data requested |
| ----------------------------------- | ----------------------------------------------------- |
| Age verification (alcohol, tobacco) | `age_over_18` or `age_over_21` |
| Identity proofing (account opening) | `family_name`, `given_name`, `birth_date`, `portrait` |
| Address verification | `resident_address`, `resident_city`, `resident_state` |
| Document validity | `expiry_date`, `document_number`, `issue_date` |
| Vehicle category check (mDL) | `driving_privileges` |
Only request the minimum data elements needed for your business process. Selective disclosure is a
core privacy feature of the mDoc standards and helps your application comply with data
minimization requirements.
When configuring your verifier, you define a **presentation request** that specifies which data
elements to request from the credential. For mDLs, presentation requests map to the ISO/IEC 18013-5
namespace (`org.iso.18013.5.1`). Other mDoc credentials use the namespaces defined by their
respective specifications (for example, ISO/IEC 23220-based credentials). Both MATTR VII and the
MATTR Pi SDKs support flexible presentation requests across these namespaces.
## Next steps [#next-steps]
With your data requirements defined, [embed the SDKs into your application](/docs/verification/embedding-the-sdks).
# Embed the SDKs into your application
URL: /docs/verification/embedding-the-sdks
MATTR provides SDKs that abstract the protocol complexity so you can focus on your user experience.
This page covers the recommended integration approach, the SDK options for each channel, and the
backend configuration remote channels require.
## Embed the SDKs into your application [#embed-the-sdks-into-your-application]
MATTR provides SDKs that abstract the protocol complexity so you can focus on your user experience.
### Integration approach [#integration-approach]
The recommended approach is to embed the MATTR Pi Verifier SDK directly into your application rather
than redirecting users to an intermediate verification service. This:
* Maintains trust continuity. Wallets validate your app's origin directly.
* Delivers a seamless user experience without navigation interruptions.
* Ensures compatibility with platform-specific security requirements.
See [implementation guidelines](/docs/verification/remote-verification-implementation-guidelines)
for detailed guidance on direct integration.
### SDK options by channel [#sdk-options-by-channel]
| Channel | SDK | Platforms |
| ------------- | ---------------------------------------------------------------------------------------- | -------------------------- |
| In-person | [MATTR Pi Verifier SDK](/docs/verification/sdks/overview) | iOS, Android, React Native |
| Remote web | [MATTR Pi Verifier Web SDK](/docs/verification/remote-web-verifiers/sdks/overview) | Browser (JavaScript) |
| Remote mobile | [MATTR Pi Verifier Mobile SDK](/docs/verification/remote-mobile-verifiers/sdks/overview) | iOS, Android, React Native |
### Backend configuration (remote channels) [#backend-configuration-remote-channels]
For remote verification (web and mobile), you also need a MATTR VII tenant configured with:
* A **verifier application** registered with your domain or app identifiers.
* A **wallet provider** configuration for each wallet ecosystem you accept.
* **Trusted issuer certificates** for the jurisdictions and credential types you support.
## Next steps [#next-steps]
Next, [establish trust with issuers](/docs/verification/establishing-trust) so your application
recognizes the credentials it receives.
# Establish trust with issuers
URL: /docs/verification/establishing-trust
For mDoc verification to work, your application must trust the issuer's signing certificates. This
page explains how trust is established, how MATTR's managed trust lists reduce your operational
burden, and how to configure trusted issuers.
For the broader picture of how trust lists work across a network (trusted issuers, trusted readers,
and trusted wallets), see [Trusted Lists](/docs/digital-trust-service/trusted-lists) in the Digital
Trust Service section.
## Establish trust with issuers [#establish-trust-with-issuers]
For mDoc verification to work, your application must trust the issuer's signing certificates. The
underlying [chain of trust](/docs/concepts/chain-of-trust) model is the same across mDoc credential
types, though the terminology differs by credential family.
### How trust works [#how-trust-works]
1. Each issuer publishes root certificates. For mDLs, these are **Issuing Authority Certificate
Authority (IACA)** roots, published at the state, territory, or national level depending on the
jurisdiction. Other mDoc credential types follow the same chain-of-trust model with their own
issuer certificate authorities.
2. Your verifier is configured with a list of trusted root certificates.
3. During verification, the SDK validates that the credential's signature chains back to a trusted
root.
4. If the chain is valid, the credential is trusted. If not, verification fails.
### Managed trust lists from MATTR [#managed-trust-lists-from-mattr]
Maintaining an up-to-date list of issuer certificates across multiple jurisdictions and credential
types is operationally complex. Certificates rotate, new issuers come online, and different
authorities may publish certificates independently.
MATTR provides **managed trust lists** that include verified IACA certificates for mDL issuers:
* **Australia**: all state and territory issuers.
* **United States**: state-level issuers participating in mDL programs.
* Additional jurisdictions as they come online.
These managed lists are kept current by MATTR, reducing your operational burden and ensuring your
application stays compatible as new issuers join the ecosystem. For non-mDL mDoc credentials, you
configure the issuer's certificates directly through the same trust APIs.
For in-person verification, trusted issuer certificates are configured locally in the SDK. For
remote verification, they are managed through the
[MATTR VII Trusted Issuers API](/docs/verification/remote-verification-api-reference/trusted-issuers).
### Configuring trusted issuers [#configuring-trusted-issuers]
* **In-person (SDK):** Load trusted root certificates into the SDK's local trust store.
* **Remote (MATTR VII):** Use the MATTR Portal or the
[Create a Trusted Issuer](/docs/verification/remote-verification-api-reference/trusted-issuers#create-a-trusted-issuer)
API to register issuer certificates on your tenant.
For deeper guidance on certificates and the chain of trust used in verification, see the
[certificates](/docs/verification/certificates/overview) pages under Establishing trust.
## Next steps [#next-steps]
Once trust is established, learn about [handling verification results](/docs/verification/handling-results).
# Frequently asked questions
URL: /docs/verification/faq
Answers to common questions about mDoc verification with MATTR. For the concepts behind these
answers, see the [verification overview](/docs/verification).
## Frequently asked questions [#frequently-asked-questions]
### What standards does mDoc verification use? [#what-standards-does-mdoc-verification-use]
mDoc verification is based on [ISO/IEC 18013-5](https://www.iso.org/standard/69084.html) for
in-person (proximity) verification and [ISO/IEC 18013-7](https://www.iso.org/standard/91154.html)
for remote (online) verification. Remote verification uses
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html) as the presentation
protocol, as specified in ISO/IEC 18013-7 Annex B. For mDoc credentials beyond the driver's license,
[ISO/IEC 23220](/docs/concepts/iso-mdoc-standards) generalizes these foundations.
### Can I verify credentials offline? [#can-i-verify-credentials-offline]
Yes. In-person verification using the MATTR Pi Verifier SDK operates over BLE and can perform full
cryptographic verification without network connectivity. Trusted issuer certificates and revocation
status lists are cached locally. Remote verification requires network connectivity.
### Which wallets are supported? [#which-wallets-are-supported]
MATTR's verification SDKs are interoperable with any wallet that implements ISO/IEC 18013-5 (for
proximity) or ISO/IEC 18013-7 with OID4VP (for remote). This includes Apple Wallet, Google Wallet,
and government-issued wallet applications. Specific wallet support is configured through
[wallet provider registrations](/docs/verification/remote-verification-api-reference/wallet-providers)
in MATTR VII.
### How do I handle expired or revoked credentials? [#how-do-i-handle-expired-or-revoked-credentials]
The verification result includes checks for credential validity period and revocation status. Your
application should treat expired or revoked credentials as failed verifications and prompt the user
to present a current credential. See
[revocation status checks](/docs/verification/in-person-guides/revocation-status-check) for
configuration details.
### What if I need to support multiple jurisdictions? [#what-if-i-need-to-support-multiple-jurisdictions]
Configure your trusted issuers list with root certificates from each jurisdiction you want to
accept. MATTR's managed trust lists simplify this for mDLs by providing pre-verified certificate
bundles for supported regions. For other mDoc credential types, register the issuer's certificates
through the same trust APIs. [Contact us](mailto:dev-support@mattr.global) to discuss your
jurisdiction requirements.
### Do I need both MATTR Pi and MATTR VII? [#do-i-need-both-mattr-pi-and-mattr-vii]
It depends on your channel:
* **In-person only:** MATTR Pi Verifier SDK is sufficient for proximity-based verification.
* **Remote (web or mobile):** You need both MATTR Pi (Verifier SDK embedded in your app) and
MATTR VII (backend service that manages trust, sessions, and verification logic).
### How long does verification take? [#how-long-does-verification-take]
In-person verification typically completes in 1-3 seconds. Remote verification depends on the user's
interaction speed with their wallet, but the cryptographic verification itself is near-instant once
the presentation is received.
### What data do I receive after verification? [#what-data-do-i-receive-after-verification]
You receive only the data elements you requested in your presentation definition. The holder must
consent to share each element. You do not receive the full credential or any data beyond what was
explicitly requested.
# Handling verification results
URL: /docs/verification/handling-results
When verification completes, your application receives a structured result. This page describes what
the result contains and how to act on both success and failure paths.
## Handling verification results [#handling-verification-results]
When verification completes, your application receives a structured result containing:
* **Verification status**: Whether the credential passed all checks.
* **Disclosed claims**: The specific data elements the holder consented to share.
* **Verification checks performed**: Signature validity, issuer trust, expiry, revocation status.
* **Failure details**: If verification failed, the specific reason.
Your application logic should handle both success and failure paths gracefully. For detailed
guidance on interpreting and acting on results, see:
* [Handling in-person verification results](/docs/verification/in-person-guides/handling-verification-results)
* [Handling remote web verification results](/docs/verification/remote-web-verifiers/guides/handling-verification-results)
## Next steps [#next-steps]
Review the [frequently asked questions](/docs/verification/faq) for answers to common verification
questions.
# In-person verification
URL: /docs/verification/in-person-overview
In-person verification is a process where a credential holder presents their credential directly to
a verifier in a proximity-based interaction. This may involve showing the credential to a person
using a verifier device or to a self-service kiosk. Engagement methods include scanning a QR code, using a secure reader, or leveraging other proximity
technologies.
In-person verification is available for the following credential formats:
* [mDocs](#mdocs)
* [CWT credentials (including Semantic CWT)](#cwt-credentials)
## mDocs [#mdocs]
### Overview [#overview]
mDocs can be verified in-person using proximity based technologies such as Bluetooth Low Energy
(BLE), and support offline verification, as defined in
[ISO/IEC 18013-5](https://www.iso.org/standard/69084.html). Refer to the
[proximity verification overview](/docs/verification/in-person-overview) for more information.
### Verification flow [#verification-flow]
Proximity (in-person) verification comprises two phases:
1. [**Engagement phase**](#engagement-phase): The interaction begins with the two devices attempting
to establish a secure communication channel for transferring data. In the context of mDocs, this
is between the holder’s and the verifier’s devices.
2. [**Data retrieval phase**](#data-retrieval-phase): Once a secure communication channel is
established, data can be securely requested and exchanged between the two devices. For mDocs,
this would be exchanging various data objects required for the verification workflow.
#### Engagement phase [#engagement-phase]
The engagement phase requires two mobile devices to:
* Become aware of one another: Enabling the holder’s device to explicitly share engagement
information with a verifier's device they wish to interact with.
* Establish a secure communication channel: As wireless technologies are essentially insecure,
additional security layers are required to prevent third parties from eavesdropping transactions.
The [ISO/IEC 18013-5:2021 standard](https://www.iso.org/standard/69084.html) defines this layer,
and it is quite similar to the usage of Transport Layer Security (TLS) for internet based
protocols.
##### Holder generates a QR code [#holder-generates-a-qr-code]
This phase is always initiated by the holder, who generates a QR code to be read by the verifier.
While it is true that any device can read this QR code, the assumption is that
the holder will only show the QR code to a verifier they trust and wish to
share information with.
The information in the QR code details:
* Wireless communication protocols supported by the holder.
* How the verifier can connect with the holder.
* Ephemeral session public key.
* Additional feature negotiations.
##### Verifier returns session key [#verifier-returns-session-key]
Assuming the verifier device supports the same protocol requirements, it will generate its own
ephemeral session public key, and attempt to establish a secure connection via a hand-shake
response.
The verifier response also includes an encrypted presentation request.
However, for clarity purposes this step is described as part of the data
retrieval phase.
##### Secure communication established [#secure-communication-established]
As the two devices connect, a unique session transcript is created and used alongside the holder and
verifier keys to derive a unique session key that is used to encrypt any exchanged data.
This session key is unique for every session and removed when the session is terminated. Any attempt
to use the same session key in a different session would fail, even if the session is between the
same two devices.
The holder will use the session key to decrypt the message (e.g. request from the verifier) and
encrypt the responses (e.g. shared mDocs).
#### Data retrieval phase [#data-retrieval-phase]
Once a secure connection is established, the following flow takes place:
##### Request [#request]
The verifier sends a presentation request. This details what type of information they wish to
verify. Note that the encrypted presentation request is actually sent alongside the verifier
ephemeral public key during the last step of the engagement phase. However, for clarity purposes
this step is described as part of the data retrieval phase.
##### Consent [#consent]
The holder receives the request and asks for the user’s consent to share the information. When
applying selective disclosure, the holder device should show the user which of their mDocs include
the required information, and enable the user to select which one to share.
##### Response [#response]
When the user agrees to share the information, a presentation is sent back to the verifier with all
of the information the user has consented to share.
##### Verification [#verification]
The verifier will check the presentation, its signature and its issuer to complete the verification
workflow with a verified/rejected conclusion.
### Verification checks [#verification-checks]
The following standard checks are performed on all mDocs verification requests:
* Issuer IACA is valid as per [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
* Credential was issued by a trusted issuer (by checking the issuer's IACA against a
[local](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/index.html#md:trusted-issuers)
or [external](/docs/digital-trust-service) list of trusted issuers).
* Digital signature is valid.
* Credential structure complies with
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
The following checks are optional and defined as part of the verification request:
* Current time is after the beginning of the credential validity period.
* Current time is not after the end of the credential validity period.
* Credential has not been revoked.
### Security considerations [#security-considerations]
The mDocs communication protocol has a further control to mitigate replay attacks, where a
credential is presented to an incorrect verifier or presented multiple times. Device and session
binding prohibit this by ensuring that a middle man hasn't poached the credential during
transmission and that the credential is only fresh and valid for the intended transaction.
## CWT credentials [#cwt-credentials]
### Overview [#overview-1]
CWT and Semantic CWT credentials are verified via Direct verification. The holder physically
presents the credential to a verifying device, which makes a direct API request to a MATTR VII
endpoint with the credential enclosed in the request body. The endpoint then verifies the presented
presented CWT or Semantic CWT credential and returns the verification result in the response.
### Verification flow [#verification-flow-1]
CWT and Semantic CWT credentials can be provided for verification in one of two formats:
* Signed credential encoded as a string. This will be the encoded element of the credential issuance
response.
* Signed credential encoded as a QR code and represented as a PDF document or an image file with the
following limitations:
* File size must be 1MB or under. Larger files are rejected with a `413` error.
* Only the first page of PDF documents is processed.
* Image files must contain a QR code of sufficient quality and resolution. This depends on many
factors such as the size of the QR relative to the image, and whether the image was processed
in any way.
* For optimal performance, ensure that only a single QR code is present on the file.
### Verification checks [#verification-checks-1]
The following standard checks are performed on all CWT or Semantic CWT credentials verification
requests:
* Conformance of the string and encoded data.
* All string representations of CWT credentials must be prefixed with CSC/1.
* All string representations of Semantic CWT credentials must be prefixed with CSS/1.
* Decoded payload structure is a valid CWT or Semantic CWT credential.
* Issuer DID can be used to resolve its DID document.
* Public key from issuer's DID document validates the proof signature, confirming the credential has
not been tampered with.
The following checks are optional and are defined as part of the verification request:
* Credential was issued by a trusted issuer.
* Current time is after the beginning of the credential validity period.
* Current time is not after the end of the credential validity period.
* Credential has not been revoked.
# In-person verification quickstart guide
URL: /docs/verification/in-person-quickstart
This quickstart is for evaluating the [MATTR Pi mDocs Verifier SDKs](/docs/verification/sdks/overview) for in-person verification. In about 15-20 minutes you will configure a sample verifier mobile app (iOS, Android, or React Native), run it on a test device and use it to verify an mDoc presented in-person from a different device using the [proximity presentation workflow](/docs/verification/in-person-overview) defined by [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
Use this guide as a fast evaluation path to see the Verifier Mobile SDK working end-to-end on real devices. For detailed information and API examples, explore the [tutorial](/docs/verification/in-person-tutorial) and reference documentation tailored for each platform.
## User experience [#user-experience]
In this quickstart you will perform this exact flow yourself using the sample verifier app and the GO Hold example app:
1. The holder uses their wallet application to generate and present a QR code.
2. The verifier scans the QR code, connects with the wallet, and requests an
mDoc for verification.
3. The wallet application displays matching credentials to the holder and asks for consent to share
them with the verifier.
4. The verifier application receives the wallet's response and verifies the provided credential.
5. Verification results are displayed to the verifier.
## Prerequisites [#prerequisites]
* Use the [Get Started form](/docs/resources/get-started) to request a trial of MATTR **verification** capabilities. You will receive access to the following resources:
* MATTR Pi mDocs Verifier SDK for your chosen platform (iOS, Android, or React Native).
* MATTR VII tenant.
* Two physical mobile devices to run the verifier and holder applications:
* Verifier device: iOS/Android mobile device to run the built Verifier application on, set up with Bluetooth access.
* Holder device: iOS/Android mobile device with the **MATTR GO Hold example app** installed for [iOS](https://apps.apple.com/app/mattr-wallet/id1518660243) or [Android](https://play.google.com/store/apps/details?id=global.mattr.wallet), set up with biometric authentication and Bluetooth access.
* Development environment set up for your chosen platform (e.g., Xcode for iOS, Android Studio for Android, or a React Native development environment).
## Steps [#steps]
In this quickstart you will:
1. Configure a local sample verifier project for your platform.
2. Run a sample verifier app and verify a credential presented in-person from a holder device.
3. Review the structure of the sample project so you can see how to integrate the SDK into your own app.
Use the tabs below to follow platform-specific setup steps.
### Create a MATTR VII backend tenant (1-3 Minutes) [#create-a-mattr-vii-backend-tenant-1-3-minutes]
The iOS and Android Verifier SDKs require a MATTR VII backend tenant. If you already have a tenant, or are evaluating the React Native SDK, you can skip this step.
1. Log into the [MATTR Portal](https://portal.mattr.global).
2. Select the **Create/switch tenant** button on the top-right side of the screen.\
The *All tenants* panel is displayed, listing any existing tenants.
3. Select the **Create new** button.\
The *New tenant* form is displayed.
4. Use the *Region* dropdown list to select the region your tenant will be hosted in.
5. Use the *Tenant subdomain* text box to insert a subdomain for your tenant (e.g. `in-person-verification`).
6. Select the **Create** button to create the new tenant.
7. Copy the displayed tenant information (`audience`, `auth_url`, `tenant_url`, `client_id` and `client_secret`) which is required for the next step.
### Create a MATTR VII Verifier Application (3-5 Minutes) [#create-a-mattr-vii-verifier-application-3-5-minutes]
If you are evaluating the iOS or Android Verifier SDK, you must create a Verifier Application on your MATTR VII tenant.
If you are evaluating the React Native Verifier SDK, you do not need to create a Verifier Application and can skip this step.
[Make a request](/docs/api-reference#getting-started) of the following structure to create an iOS Verifier Application configuration on your MATTR VII tenant **(make sure to replace `bundleId` and `teamId` with your own values)**:
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "My iOS Verifier Application",
"type": "ios",
"bundleId": "com.yourcompany.verifierapp",
"teamId": "YOUR_APPLE_TEAM_ID",
"appAttest": {
"required": false,
"environment": "development"
}
}
```
* `bundleId`: This must match the bundle identifier of your iOS application you will create in the next step.
* `teamId`: This must match the Apple Developer Team ID that actually signs this app.
A successful response returns a `201` status code with the created Verifier Application:
```json title="Response"
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c", // [!code focus]
"name": "My iOS Verifier Application",
"type": "ios",
"bundleId": "com.yourcompany.verifierapp",
"teamId": "YOUR_APPLE_TEAM_ID",
"appAttest": {
"required": false,
"environment": "development"
}
}
```
* `id`: A unique identifier for the Verifier Application (generated by the tenant). You must use this value when initializing the SDK so that it can correctly
identify and authenticate your application.
[Make a request](/docs/api-reference#getting-started) of the following structure to create an Android Verifier Application configuration on your MATTR VII tenant:
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "My Android Verifier Application",
"type": "android",
"packageName": "com.yourcompany.verifierapp",
"packageSigningCertificateThumbprints": [
"1232584B6F6A892D356899FB9576C5F226A179E6199F2B7A1D837B5C234C5A8E"
],
"keyAttestation": {
"required": false
},
"openid4vpConfiguration": {
"redirectUri": "com.yourcompany.verifierapp://oid4vp-callback"
}
}
```
A successful response returns a `201` status code with the created Verifier Application:
```json title="Response"
{
"id": "a82bfa46-72a0-4cde-b6cb-2a0de7e2f3c4", // [!code focus]
"name": "My Android Verifier Application",
"type": "android",
"packageName": "com.yourcompany.verifierapp",
"packageSigningCertificateThumbprints": [
"1232584B6F6A892D356899FB9576C5F226A179E6199F2B7A1D837B5C234C5A8E"
],
"keyAttestation": {
"required": false
}
}
```
* `id`: A unique identifier for the Verifier Application (generated by the tenant). You must use this value when initializing the SDK so that it can correctly
identify and authenticate your application.
This step is currently not required for the React Native Verifier SDK.
### Configure the sample verifier project (5-10 Minutes) [#configure-the-sample-verifier-project-5-10-minutes]
1. Access the sample verifier codebase by either:
* Cloning the [MATTR Sample Apps repository](https://github.com/mattrglobal/sample-apps):
```bash title="Clone sample apps"
git clone https://github.com/mattrglobal/sample-apps.git
```
* Or downloading just the iOS sample verifier project using [download-directory.github.io](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2Fmattrglobal%2Fsample-apps%2Ftree%2Fmaster%2Fios-in-person-verifier-tutorial-sample-app).
2. Use Xcode to open the `.xcodeproj` file in the project's folder. You can find it in the `sample-apps/ios-in-person-verifier-tutorial-sample-app` directory.
3. Drag the `MobileCredentialVerifierSDK.xcframework` folder (obtained from MATTR as part of the SDK package) into your project.
4. Configure `MobileCredentialVerifierSDK.xcframework` to
[Embed & sign](https://help.apple.com/xcode/mac/current/#/dev51a648b07).
5. Set a unique bundle identifier for the project in the Xcode project settings (e.g., `com.yourname.proximityverifier`). This must match the `bundleId` you used when creating the Verifier Application in the [Create a MATTR VII Verifier Application](#create-a-mattr-vii-verifier-application-3-5-minutes) step.
6. Open the `Constants.swift` file and update it with your tenant and application details:
```swift title="Constants.swift"
static let tenantHost = URL(string: "https://your-tenant.vii.mattr.global")!
static let applicationId = ""
```
* `tenantHost`: Replace with the URL of your MATTR VII tenant, available in the MATTR Portal under **Platform Management > Tenant**.
* `applicationId`: Replace with the `id` returned when you created the Verifier Application in the [Create a MATTR VII Verifier Application](#create-a-mattr-vii-verifier-application-3-5-minutes) step.
7. Run the project on a physical iOS device (simulators do not support the required Bluetooth capabilities).
1. Access the sample verifier codebase by either:
* Cloning the [MATTR Sample Apps repository](https://github.com/mattrglobal/sample-apps):
```bash title="Clone sample apps"
git clone https://github.com/mattrglobal/sample-apps.git
```
* Or downloading just the Android sample verifier project using [download-directory.github.io](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2Fmattrglobal%2Fsample-apps%2Ftree%2Fmaster%2Fandroid-in-person-verifier-tutorial-sample-app).
2. Open the project in Android Studio. You can find it in the `sample-apps/android-in-person-verifier-tutorial-sample-app` directory.
3. Unzip the `mobile-credential-verifier-*version*.zip` file (obtained from MATTR as part of the SDK package).
4. Drag the unzipped `global` folder into the project's `repo` folder.
5. Sync Project with Gradle files to recognize the new module.
6. Open the `Constants.kt` file and update it with your tenant and application details:
```kotlin title="Constants.kt"
const val TENANT_HOST = "https://your-tenant.vii.mattr.global"
const val APPLICATION_ID = ""
```
* `TENANT_HOST`: Replace with the URL of your MATTR VII tenant, available in the MATTR Portal under **Platform Management > Tenant**.
* `APPLICATION_ID`: Replace with the `id` returned when you created the Verifier Application in the [Create a MATTR VII Verifier Application](#create-a-mattr-vii-verifier-application-3-5-minutes) step.
7. Run the project on a physical Android device (emulators do not support the required Bluetooth capabilities).
8. Retrieve your app's signing certificate thumbprint and convert it to the required format:
* Open a terminal inside your project's root folder and run:
```bash title="Retrieve signing certificate"
./gradlew signingReport
```
If you see `permission denied: ./gradlew`, the Gradle wrapper has lost its executable bit (this
happens when the project is downloaded as a zip rather than cloned). Make it executable and run
the command again:
```bash title="Fix wrapper permissions"
chmod +x gradlew
```
* Locate the `SHA-256` value in the `Variant: debug` section, remove all colons (`:`), and
convert all letters to lowercase. The result is the thumbprint you will send to your tenant in
the next step:
```bash title="Example conversion"
echo '91:F7:CB:F9:D6:81:53:1B:C7:A5:8F:B8:33:CC:A1:4D:AB:ED:E5:09:C5' | tr -d ':' | tr 'A-F' 'a-f'
```
This thumbprint is only valid for your debug builds. If you intend to publish your app, repeat
this process with the release signing certificate. Refer to
[Android App Signing](/docs/verification/android-app-signing) for more information.
9. Update your Verifier Application with the formatted thumbprint by making a request of the following structure. Use the application `id` returned in the [Create a MATTR VII Verifier Application](#create-a-mattr-vii-verifier-application-3-5-minutes) step as the `{applicationId}` path parameter, and set `packageSigningCertificateThumbprints` to the thumbprint you generated in the previous step:
```http title="Request"
PUT /v2/presentations/applications/{applicationId}
```
```json title="Request body"
{
"name": "My Android Verifier Application",
"type": "android",
"packageName": "com.yourcompany.verifierapp",
"packageSigningCertificateThumbprints": [
"91f7cbf9d681531bc7a58fb833cca14dabede509c5"
],
"keyAttestation": {
"required": false
},
"openid4vpConfiguration": {
"redirectUri": "com.yourcompany.verifierapp://oid4vp-callback"
}
}
```
The `PUT` request replaces the entire application configuration, so include all the fields you
set when you created the application in the [Create a MATTR VII Verifier Application](#create-a-mattr-vii-verifier-application-3-5-minutes) step, not just the thumbprint.
A successful response returns a `200` status code with the updated Verifier Application:
```json title="Response"
{
"id": "a82bfa46-72a0-4cde-b6cb-2a0de7e2f3c4",
"name": "My Android Verifier Application",
"type": "android",
"packageSigningCertificateThumbprints": [
"91f7cbf9d681531bc7a58fb833cca14dabede509c5"
],
// ... rest of application configuration
}
```
1. Access the completed sample verifier codebase by either:
* Cloning the MATTR sample-apps repository:
```bash title="Clone the repository"
git clone https://github.com/mattrglobal/sample-apps.git
```
* Or downloading just the React Native sample verifier project using the
[download-directory.github.io](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2Fmattrglobal%2Fsample-apps%2Ftree%2Fmaster%2Freact-native-in-person-verifier-tutorial%2Freact-native-in-person-verifier-tutorial-complete)
utility.
2. Open the completed sample verifier project in your code editor. You can find it in the
`sample-apps/react-native-in-person-verifier-tutorial/react-native-in-person-verifier-tutorial-complete`
directory.
3. Open the `app.config.ts` file.
4. Update the iOS `bundleIdentifier` to a unique value for your application, e.g. `com.mycompany.myapp`.
```typescript title="app.config.ts"
bundleIdentifier: "com.mycompany.myapp",
```
5. Update the Android `package` to a unique value, e.g. `com.mycompany.myapp`:
```ts title="app.config.ts"
package: "com.mycompany.myapp",
```
6. Navigate to the starter project directory and install dependencies:
```bash title="Install dependencies"
yarn install
```
You must be logged in to npm with access to the MATTR Pi mDocs Verifier SDK package to install dependencies successfully. If you have not yet been granted access to the SDK, apply for access [here](/docs/resources/get-started).
7. Run the following command to generate the iOS and Android project files:
```bash title="Generate project files"
yarn expo prebuild
```
You should now see the `ios` and `android` folders in your project root.
8. Connect your testing device(s) and run the following command(s) to start the application(s):
**iOS**
```bash title="Run iOS application"
yarn ios --device
```
**Android**
```bash title="Run Android application"
yarn android --device
```
### Verify a credential using the sample verifier app (3 Minutes) [#verify-a-credential-using-the-sample-verifier-app-3-minutes]
1. On your **holder testing device**, launch the **MATTR GO Hold example app**.
2. Tap the Blue **Share** button.
3. Select **Respond or Collect**. This will open the camera view (You may need to allow the app to
access your camera).
4. Scan the following QR code:
5. Follow the on-screen instructions to claim the credential (Note that this workflow requires an active internet connection).
6. Once the credential is claimed, select the Blue **Share** button again.
7. Select **Share Credential** and then choose the **Connection QR** tab to display a QR code.
8. On your **verifier testing device**, launch the sample verifier app and select the **Scan QR Code** button.
9. Use the **verifier testing device** to scan the QR code displayed on the **holder testing device**.
10. On your **holder testing device**, consent to sharing the information with the verifier.
11. On your **verifier testing device**, review the verification results displayed in the app.
Behind the scenes, the Verifier Mobile SDK handles device engagement, secure session establishment, and verification of the mDoc according to ISO/IEC 18013-5:2021.
## Review the codebase [#review-the-codebase]
The sample verifier application uses the [Verifier Mobile SDK](/docs/verification/sdks/overview) to verify mDocs presented in-person from a wallet. Once you have the sample application running, use this section to inspect the key SDK calls you will reuse in your own application.
The sample projects include code comments that explain the purpose of each step and how it maps to the in-person verification workflow. The key steps for handling in-person verification with the SDK are outlined below, along with links to the relevant API reference documentation for each platform.
### Initialize the SDK [#initialize-the-sdk]
In the sample app, SDK initialization happens once during startup so the verifier can handle in-person sessions. On iOS and Android, [SDK Tethering](/docs/verification/sdks/sdk-tethering) is required, so the SDK is initialized with a platform configuration that points at your MATTR VII tenant and Verifier Application. This registers the app instance and obtains a license on first launch.
`initialize` is asynchronous and takes a `PlatformConfiguration`. It can throw `failedToRegister` and `invalidLicense` when tethering fails:
```swift title="Initialize the SDK"
let platformConfiguration = PlatformConfiguration(
tenantHost: URL(string: "https://your-tenant.vii.mattr.global")!,
applicationId: ""
)
try await mobileCredentialVerifier.initialize(platformConfiguration: platformConfiguration)
```
`initialize` takes a `PlatformConfiguration` and can throw `FailedToRegisterException` and `InvalidLicenseException` when tethering fails:
```kotlin title="Initialize the SDK"
val platformConfiguration = PlatformConfiguration(
tenantHost = URL("https://your-tenant.vii.mattr.global"),
applicationId = ""
)
MobileCredentialVerifier.initialize(activity, platformConfiguration)
```
The React Native Verifier SDK is not tethered, so initialization needs no platform configuration:
```tsx title="Initialize the SDK"
const result = await initialize();
if (result.isErr()) {
// Handle error: result.error
}
```
### Adding a trusted issuer certificate [#adding-a-trusted-issuer-certificate]
In the sample app, a trusted issuer certificate is added during startup after SDK initialization. This allows the verifier to validate the authenticity of credentials issued by trusted issuers.
```swift title="Add a trusted issuer certificate"
try await mobileCredentialVerifier.addTrustedIssuerCertificates(certificates: [certificate])
```
* `certificate` : Pass the IACA certificate of an issuer you want your application to trust.
```kotlin title="Add a trusted issuer certificate"
MobileCredentialVerifier.addTrustedIssuerCertificates(certificates = listOf(certificate))
```
* `certificate` : Pass the IACA certificate of an issuer you want your application to trust.
```tsx title="Add a trusted issuer certificate"
await mobileCredentialVerifier.addTrustedIssuerCertificates([certificate])
```
* `certificate` : Pass the IACA certificate of an issuer you want your application to trust.
### Create a presentation request object [#create-a-presentation-request-object]
Before sending a presentation request to the holder, the verifier application needs to create a request object that defines the type of credential and specific attributes being requested for verification.
```swift title="Create a presentation request"
let mobileCredentialRequest = MobileCredentialRequest(
docType: "",
namespaces: [
"": [
"": false,
"": false,
"": false
]
]
)
```
* `docType` : The type of credential being requested (e.g., an mDL).
* `namespaces` : A dictionary mapping each namespace to its requested attributes. Each attribute is
a key with a Boolean indicating whether the verifier intends to retain this attribute (`true`) or
not (`false`).
```kotlin title="Create a presentation request"
val mobileCredentialRequest = MobileCredentialRequest(
docType = "",
namespaces = NameSpaces(
mapOf(
"" to DataElements(
mapOf(
"" to false,
"" to false,
"" to false
)
)
)
)
)
```
* `docType` : The type of credential being requested (e.g., an mDL).
* `namespaces` : A map of requested attributes under each namespace, where false indicates the
verifier does not intent to retain this attribute.
```tsx title="Create a presentation request"
const mobileCredentialRequest = {
docType: '',
namespaces: {
'': {
'': false,
'': false,
'': false
}
}
}
```
* `docType` : The type of credential being requested (e.g., an mDL).
* `namespaces` : An object that defines the attributes being requested for each namespace. A value
of `false` indicates the verifier does not intent to retain this attribute.
### Create a proximity presentation session [#create-a-proximity-presentation-session]
Once the verifier application receives a device engagement string (e.g., from scanning a QR code presented by the holder), it can create a proximity presentation session to establish a secure connection with the holder's wallet and send presentation requests.
1. Extend a class with the
[`ProximityPresentationSessionListener`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/proximitypresentationsessionlistener)
protocol:
```swift title="Conform to ProximityPresentationSessionListener"
extension VerifierViewModel: ProximityPresentationSessionListener {
public func onEstablished() {
// Handle session establishment: show presentation session details or send a request.
}
public func onTerminated(error: (any Error)?) {
/// Handle session termination
print("Session Terminated")
}
}
```
2. Call the
[`createProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/mobilecredentialverifier/createproximitypresentationsession\(encodeddeviceengagementstring:listener:\))
function with an instance of the extended class:
```swift title="Create a proximity presentation session"
mobileCredentialVerifier.createProximityPresentationSession(encodedDeviceEngagementString: deviceEngagementString, listener: self)
```
* `encodedDeviceEngagementString` : Pass the device engagement string retrieved from a QR code
presented by the holder.
* `listener` : An object that conforms to `ProximityPresentationSessionListener` protocol and will
handle the session events.
1. Implement the
[`ProximityPresentationSessionListener`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/m-docs%20-verifier%20-s-d-k/global.mattr.mobilecredential.verifier/-proximity-presentation-session-listener/index.html)
interface:
```kotlin title="Implement ProximityPresentationSessionListener"
val sessionListener = object : ProximityPresentationSessionListener {
override fun onEstablished() {
// Handle session establishment: show presentation session details or send a request.
}
override fun onTerminated(error: Throwable?) {
Log.d("SessionListener", "Session Terminated")
}
}
```
2. Call the
[`createProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/m-docs%20-verifier%20-s-d-k/global.mattr.mobilecredential.verifier/-mobile-credential-verifier/create-proximity-presentation-session.html?query=fun%20createProximityPresentationSession\(activity:%20Activity,%20qrEncodedDeviceEngagementString:%20String,%20listener:%20ProximityPresentationSessionListener\))
function with an instance of the extended class:
```kotlin title="Create a proximity presentation session"
MobileCredentialVerifier.createProximityPresentationSession(activity, deviceEngagementString, sessionListener)
```
* `deviceEngagementString` : Pass the device engagement string retrieved from a QR code presented by
the holder.
* `sessionListener` : An object that implements `ProximityPresentationSessionListener` interface and
will handle the session events.
```tsx title="Create a proximity presentation session"
const proximityPresentationSession =
await mobileCredentialVerifier.createProximityPresentationSession({
encodedDeviceEngagementString: encodedDeviceEngagementString,
onSessionTerminated: (error) => {
// Handle session termination or error
}
})
```
* `encodedDeviceEngagementString` : Pass the device engagement string retrieved from a QR code
presented by the holder.
* `onSessionTerminated` : A callback triggered when the session ends or fails.
The
[`createProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/functions/createProximityPresentationSession.html)
method returns an empty success response, indicating that the session has been created and the
verifier application can send a presentation request to the holder.
### Listen to NFC session requests [#listen-to-nfc-session-requests]
To support NFC-based device engagement in addition to QR code scanning, the verifier application can listen for NFC session requests. When an NFC session request is received, the SDK will automatically create a proximity presentation session and trigger the appropriate session listener callbacks.
This capability is currently only available in the Android SDK.
```kotlin title="Start listening to NFC session requests"
MobileCredentialVerifier.registerForNfcDeviceEngagement(activity, sessionListener)
```
* `sessionListener` : An object that implements the `ProximityPresentationSessionListener` interface
and will handle the session events.
```kotlin title="Stop listening to NFC session requests"
MobileCredentialVerifier.deregisterForNfcDeviceEngagement(activity)
```
This capability is currently only available in the Android SDK.
### Send a presentation request [#send-a-presentation-request]
After a proximity presentation session is established (e.g., after scanning a QR code or receiving an NFC engagement), the verifier application can send a presentation request to the holder's wallet to request specific credentials for verification.
```swift title="Send a presentation request"
try await mobileCredentialVerifier.sendProximityPresentationRequest(request: [mobileCredentialRequest], checkStatus: true)
```
* `request` : Pass the
[`MobileCredentialRequest`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/mobilecredentialrequest)
object created in the previous step.
* `checkStatus` : A Boolean indicating whether to check credential revocation status. Set to
`true` (default) to check credential revocation status as part of the verification, or `false` to skip status checking.
```kotlin title="Send a presentation request"
MobileCredentialVerifier.sendProximityPresentationRequest(request = listOf(mobileCredentialRequest), checkStatus = true)
```
* `request` : Pass the
[`MobileCredentialRequest`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/m-docs%20-verifier%20-s-d-k/global.mattr.mobilecredential.verifier.dto/-mobile-credential-request/index.html?query=data%20class%20MobileCredentialRequest\(val%20docType:%20DocType,%20val%20namespaces:%20NameSpaces\))
object created in the previous step.
* `checkStatus` : A Boolean indicating whether to check credential revocation status. Set to
`true` (default) to check credential revocation status as part of the verification, or `false` to skip status checking.
```tsx title="Send a presentation request"
const mobileCredentialResponse =
await proximityPresentationSession.sendProximityPresentationRequest({
request: mobileCredentialRequest,
checkStatus: true
})
```
* `request` : Pass the
[`MobileCredentialRequest`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/types/MobileCredentialRequest.html)
object created two steps up.
* `checkStatus` : A Boolean indicating whether to check credential revocation status. Set to `true` (default) to check
credential revocation status as part of the verification, or `false` to skip status checking.
### Handle the presentation response [#handle-the-presentation-response]
After sending a presentation request, the verifier application will receive a response from the holder's wallet containing the credentials that match the request along with their verification status. The application can then handle this response to display verification results to the user.
The
[`sendProximityPresentationRequest`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/mobilecredentialverifier/sendproximitypresentationrequest\(request:checkstatus:\))
method returns a
[`MobileCredentialResponse`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/mobilecredentialresponse)
object. This response contains the presentation details provided by the holder, including any errors
encountered and the verification status of the credentials returned in the response:
```swift title="MobileCredentialResponse"
let mobileCredentialResponse = MobileCredentialResponse(
credentialErrors: [/* CredentialError */],
credentials: [
MobileCredentialPresentation(
branding: /* Branding information for displaying the credential */,
claimErrors: [
/* Namespace */: [
/* ElementID */: /* MobileCredentialResponseErrorCode */
]
],
claims: [
/* Namespace */: [
/* ElementID */: /* MobileCredentialElementValue */
]
],
docType: /* DocType */,
issuerInfo: /* IssuerInfo */,
validityInfo: /* Validity */,
verificationResult: /* VerificationResult */
)
]
)
```
* `credentialErrors` : Any requested docTypes not returned by the holder.
* `credentials` : A list of presented credentials with their data, status, and any
attribute-specific errors.
The
[`sendProximityPresentationRequest`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/m-docs%20-verifier%20-s-d-k/global.mattr.mobilecredential.verifier/-mobile-credential-verifier/send-proximity-presentation-request.html)
method returns a
[`MobileCredentialResponse`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/m-docs%20-verifier%20-s-d-k/global.mattr.mobilecredential.verifier.dto/-mobile-credential-response/index.html)
object. This response contains the presentation details provided by the holder, including any errors
encountered and the verification status of the credentials returned in the response:
```kotlin title="MobileCredentialResponse structure"
data class MobileCredentialResponse(
val credentials: List,
val credentialErrors: List
)
```
```kotlin title="MobileCredentialPresentation structure"
data class MobileCredentialPresentation(
val docType: DocType,
val validityInfo: MobileCredentialValidity,
val claimErrors: Map>?,
val claims: Map>?,
val branding: Branding?,
val issuerInfo: IssuerInfo?,
val verificationResult: MobileCredentialVerificationResult
)
```
```kotlin title="CredentialError structure"
data class CredentialError(
val docType: DocType,
val errorCode: ErrorCode
)
typealias DocType = String
typealias ErrorCode = Int
```
* `credentialErrors` : Any requested docTypes not returned by the holder.
* `credentials` : A list of presented credentials with their data, status, and any
attribute-specific errors.
The
[`sendProximityPresentationRequest`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/functions/sendProximityPresentationRequest.html)
returns a
[`MobileCredentialResponse`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/types/MobileCredentialResponse.html)
object. This response contains the presentation details provided by the holder, including any errors
encountered and the verification status of the credentials returned in the response:
```tsx title="MobileCredentialResponse"
const mobileCredentialResponse = {
credentialErrors: [/* CredentialError */],
credentials: [
{
branding: /* Branding */,
claimErrors: {
/* Namespace */: {
/* ElementID */: /* MobileCredentialResponseErrorCode */
}
},
claims: {
/* Namespace */: {
/* ElementID */: /* MobileCredentialElementValue */
}
},
docType: /* DocType */,
issuerInfo: /* IssuerInfo */,
validityInfo: /* Validity */,
verificationResult: /* VerificationResult */
}
]
};
```
* `credentialErrors` : Any requested docTypes not returned by the holder.
* `credentials` : A list of presented credentials with their data, status, and any
attribute-specific errors.
## Next steps [#next-steps]
* For your evaluation:
* Note how long it took to configure and run the sample verifier app and complete an in-person verification.
* Consider how the sample’s presentation request and UI map to your real in-person verification use cases (for example, which attributes you would request and how you would display them).
* Explore the [in-person verification tutorial](/docs/verification/in-person-tutorial) for detailed instructions, configuration options, and production-grade patterns.
* Review the [mDocs Verifier SDK overview](/docs/verification/sdks/overview) to understand platform support, capabilities, and how proximity and remote verification relate.
# Learn how to build an application that can verify an mDoc presented via a proximity workflow
URL: /docs/verification/in-person-tutorial
## Overview [#overview]
In this tutorial you will use the
[mDocs mobile verifier SDKs](/docs/verification/remote-mobile-verifiers/sdks/overview) to build an application
that can verify an [mDoc](/docs/concepts/mdocs) presented via a
[proximity workflow](/docs/verification/in-person-overview) as per
[ISO 18013-5](https://www.iso.org/standard/69084.html):
1. The credential holder presents a QR code generated by their wallet application.
2. The verifier uses their application to scan the QR code, connect with the wallet and request an
mDoc for verification.
3. The wallet application displays matching credentials to the holder and asks for consent to share
them with the verifier.
4. The verifier application receives the wallet's response and verifies the provided credential.
5. Verification results are displayed to the verifier.
The result will look something like this:
To achieve this, you will build the following capabilities into your verifier application:
* Initialize the SDK, so that your application can use its functions and classes.
* Register a trusted issuer certificate, which enables your application to verify mDocs issued by
that issuer.
* Scan a QR code presented by a wallet application and establish a secure communication channel.
* Send presentation requests to the wallet application, receive a presentation response and verify
its content.
* Display the results to the verifier app user.
## Prerequisites [#prerequisites]
Before we get started, let's make sure you have everything you need.
### Prior knowledge [#prior-knowledge]
* The proximity verification workflow described in this tutorial is based on the
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html) standard. If you are unfamiliar
with this standard, refer to the following Docs for more information:
* What are [mDocs](/docs/concepts/mdocs)?
* What is [credential verification](/docs/verification)?
* Breakdown of the [proximity presentation workflow](/docs/verification/in-person-overview).
* We assume you have experience developing applications in the relevant programming languages and
frameworks (Swift for iOS, Kotlin for Android, and JavaScript/TypeScript for React Native).
If you need to get a verifier solution up and running quickly with minimal
development resources and in-house domain expertise, [talk to
us](http://mattr.global/contact-us) about our white-label [MATTR GO
Verify](https://mattr.global/platforms/go) which might be a good fit for you.
### Assets [#assets]
* Use the [Get Started form](/docs/resources/get-started) to request a trial of MATTR **verification** capabilities. You will receive access to the following resources:
* MATTR Pi mDocs Verifier SDK for your chosen platform (iOS, Android, or React Native).
* MATTR VII tenant.
* As part of your onboarding process you will be provided with access to the following
assets:
* ZIP file which includes the required framework:
(`MobileCredentialVerifierSDK.xcframework.zip`).
* Sample Verifier app: You can use this app for reference as we work through this tutorial.
This tutorial is only meant to be used with the most [recent
version](/docs/verification/sdks/overview#versions)
of the iOS mDocs Verifier SDK.
* Use the [Get Started form](/docs/resources/get-started) to request a trial of MATTR **verification** capabilities. You will receive access to the following resources:
* MATTR Pi mDocs Verifier SDK for your chosen platform (iOS, Android, or React Native).
* MATTR VII tenant.
* As part of your onboarding process you will be provided with access to the following
assets:
* A ZIP file that includes the required library (`mobile-credential-verifier-*version*.zip`).
* Sample Verifier app: You can use this app for reference as we work through this tutorial.
This tutorial is only meant to be used with the most [recent
version](/docs/verification/sdks/overview#versions)
of the Android mDocs Verifier SDK.
* Use the [Get Started form](/docs/resources/get-started) to request a trial of MATTR **verification** capabilities. You will receive access to the following resources:
* MATTR Pi mDocs Verifier SDK for your chosen platform (iOS, Android, or React Native).
* MATTR VII tenant.
* As part of your onboarding process you will be provided with access to the following
assets:
* Access to the [@mattrglobal/mobile-credential-verifier-react-native](https://www.npmjs.com/package/@mattrglobal/mobile-credential-verifier-react-native) npm package.
* Sample Verifier app: You can use this app for reference as we work through this tutorial.
This tutorial is only meant to be used with the most [recent version](/docs/verification/sdks/overview#versions) of the React Native mDocs Verifier SDK.
### Development environment [#development-environment]
* [Xcode](https://developer.apple.com/xcode/) setup with either:
* Local build settings if you are developing locally.
* [iOS developer account](https://developer.apple.com/programs/enroll/) if you intend to publish
your app.
* [Android Studio](https://developer.android.com/studio/).
* Code editor (such as [VS Code](https://code.visualstudio.com/download)).
* [Android Studio](https://developer.android.com/studio/).
* [Xcode](https://developer.apple.com/xcode/).
* [yarn](https://yarnpkg.com/) (v1.22.22 was used during development).
* Java v17.
This tutorial uses [Expo Go](https://expo.dev/go), leveraging [Development
Builds](https://docs.expo.dev/develop/development-builds/introduction/).
### Testing devices [#testing-devices]
As this tutorial implements a proximity presentation workflow, you will need two different mobile
devices to test the end-to-end result:
* Verifier device:
* Supported iOS device to run the built Verifier application on, setup with:
* Bluetooth access.
* Available internet connection.
* Holder device:
* Mobile device with the
[MATTR GO Hold example app](/docs/holding/go-hold/getting-started) installed and
setup with:
* Biometric authentication.
* Bluetooth access.
* Available internet connection.
* Verifier device:
* Supported Android device to run the built Verifier application on, setup with:
* Bluetooth access.
* Available internet connection.
* [USB debugging](https://developer.android.com/studio/debug/dev-options#enable) enabled.
* Holder device:
* Mobile device with the
[MATTR GO Hold example app](/docs/holding/go-hold/getting-started) installed and
setup with:
* Biometric authentication.
* Bluetooth access.
* Available internet connection.
* Verifier device:
* Supported iOS or Android device to run the built Verifier application on, setup with:
* Bluetooth access.
* Available internet connection.
* Holder device:
* Mobile device with the
[MATTR GO Hold example app](/docs/holding/go-hold/getting-started) installed and
setup with:
* Biometric authentication.
* Bluetooth access.
* Available internet connection.
### Testing credential [#testing-credential]
You will need a test credential to verify during this tutorial. You can use the MATTR GO Hold example app to claim a test mDoc by following these steps:
1. Download and install the
[MATTR GO Hold example app](/docs/holding/go-hold/getting-started) on your **holder
testing device**.
2. Launch the **MATTR GO Hold example app**.
3. Tap the Blue **Share** button.
4. Select **Respond or Collect**. This will open the camera view (You may need to allow the app to
access your camera).
5. Scan the following QR code:
6. Follow the on-screen instructions to claim the credential (Note that this workflow requires an active internet connection).
Got everything? Let's get going!
## Environment setup [#environment-setup]
Perform the following steps to setup and configure your development environment:
**Step 1: Create a new project**
Please follow the detailed instructions to
[Create a new Xcode Project](https://help.apple.com/xcode/mac/current/#/dev07db0e578) and add your
organization's identifier.
**Step 2: Unzip the dependencies file**
1. Unzip the [`MobileCredentialVerifierSDK.xcframework.zip` file](#assets).
2. Drag the `MobileCredentialVerifierSDK.xcframework` folder into your project.
3. Configure `MobileCredentialVerifierSDK.xcframework` to
[Embed & sign](https://help.apple.com/xcode/mac/current/#/dev51a648b07).
See [Add existing files and folders](https://help.apple.com/xcode/mac/current/#/dev81ce1d383) for
detailed instructions.
This should result in the the following framework being added to your project:
**Step 3: Add Bluetooth permissions**
The SDK requires access to the mobile device Bluetooth capabilities as part of the proximity
presentation workflow.
[Configure these permissions](https://help.apple.com/xcode/mac/current/#/dev37c2f42ff) in the `Info`
tab of the Application target:
**Step 4: Run the application**
Select **Run** and make sure the application launches with a *“Hello, world!”* text in the middle of
the display, as shown in the following image:
**Step 1: Create a new project**
1. [Create a new Android Studio project](https://developer.android.com/studio/projects/create-project),
using the *Empty Activity* template.
2. Name the project `Verifier Tutorial`.
3. Select *API 24* as the `Minimum SDK` version.
4. Select *Kotlin DSL* as the `Build configuration language`.
5. Select **Finish**.
6. [Sync](https://developer.android.com/build#sync-files) the project with Gradle files.
**Step 2: Add required dependencies**
1. Select the [Project view](https://developer.android.com/studio/projects#ProjectView).
2. Create a new directory named `repo` in your project's folder.
3. Unzip the `mobile-credential-verifier-*version*.zip` file and copy the unzipped
`global` folder into the new `repo` folder.
4. Open the `settings.gradle.kts` file in the `VerifierTutorial` folder and add the following Maven
repository to the `dependencyResolutionManagement.repositories` block:
```kotlin title="settings.gradle.kts"
maven {
url = uri("repo")
}
```
5. Open the `app/build.gradle.kts` file in your app folder and add the following dependencies to the
`dependencies` block:
```kotlin title="app/build.gradle.kts"
implementation("global.mattr.mobilecredential:verifier:7.0.0")
implementation("androidx.navigation:navigation-compose:2.9.0")
```
* The `verifier` dependency version should match the version of the unzipped `mobile-credential-verifier-*version*.zip` file you copied to the `repo` folder.
* The required `navigation-compose` version may differ based on your version of the IDE, Gradle, and other project dependencies.
6. Open the `gradle/libs.versions.toml` file and pin the following libraries versions:
```kotlin title="gradle/libs.versions.toml"
coreKtx = "1.18.0"
lifecycleRuntimeKtx = "2.10.0"
```
7. [Sync](https://developer.android.com/build#sync-files) the project with Gradle files.
8. Open the [Build](https://developer.android.com/studio/run#gradle-console) tab and select `Sync`
to make sure that the project has synced successfully.
**Step 3: Run the application**
1. Connect a [debuggable](https://developer.android.com/studio/debug/dev-options#Enable-debugging)
mobile device to your machine.
2. [Build and run the app](https://developer.android.com/studio/run) on the connected mobile
device.
The app should launch with a *“Hello, Android!”* text displayed:
**Step 1: Access the tutorial codebase**
1. Access the tutorial starter codebase by either:
* Cloning the MATTR sample-apps repository:
```bash title="Clone the repository"
git clone https://github.com/mattrglobal/sample-apps.git
```
or
* Downloading just the starter directory using the
[download-directory.github.io](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2Fmattrglobal%2Fsample-apps%2Ftree%2Fmaster%2Freact-native-in-person-verifier-tutorial%2Freact-native-in-person-verifier-tutorial-starter)
utility.
2. Open the tutorial project in your code editor. You can find it in the
`sample-apps/react-native-in-person-verifier-tutorial/react-native-in-person-verifier-tutorial-starter/`
directory.
You can find the completed tutorial code in the `sample-apps/react-native-in-person-verifier-tutorial/react-native-in-person-verifier-tutorial-complete`
directory and use it as a reference as you work along this tutorial.
**Step 2: iOS Application configuration**
1. Open the `app.config.ts` file and update the `bundleIdentifier` value under the
`// Update the bundle identifier` comment to a unique value for your application, e.g.
`com.mycompany.myapp`.
```typescript title="app.config.ts"
bundleIdentifier: "com.mycompany.myapp",
```
iOS requires each app to have a unique bundle identifier for App Store and development
environments.
2. Add the following camera and Bluetooth permissions to the `ios.infoPlist` object under the
`// Add necessary permissions for camera and Bluetooth` comment:
```ts title="app.config.ts"
NSCameraUsageDescription: "Camera is used to scan QR codes.",
NSBluetoothAlwaysUsageDescription: "This app uses Bluetooth to communicate with verifiers or holders.",
NSBluetoothPeripheralUsageDescription: "This app uses Bluetooth to communicate with verifiers or holders.",
```
These permissions are required for the app to use the camera for QR
code scanning, and Bluetooth for proximity communication with the holder's app.
**Step 3: Configure the app plugins**
Add the following code under the `// Configure the app plugins` comment to import required plugin
configurations:
```ts title="app.config.ts"
"./withMobileCredentialAndroidVerifierSdk",
[
"expo-build-properties",
{
android: {
minSdkVersion: 24,
compileSdkVersion: 36,
targetSdkVersion: 34,
kotlinVersion: "2.0.21",
},
},
],
[
"expo-camera",
{
cameraPermission: "Allow $(PRODUCT_NAME) to access your camera",
},
],
```
The SDK requires platform-specific configurations to work correctly. A plugin file specifically for Android has already
been created in your project root directory. You can also follow the instructions in the
[mDocs Verifier](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/index.html#md:platform-android)
SDK Docs to perform this platform-specific configuration manually.
**Step 4: Install the dependencies**
1. Open a terminal in the project's root and navigate to the starter project directory:
```bash title="Navigate to the project directory"
cd sample-apps/react-native-in-person-verifier-tutorial/react-native-in-person-verifier-tutorial-starter/
```
2. Install the application dependencies:
```bash title="Install dependencies"
yarn install
```
**Step 5: Generate the iOS and Android project files**
Run the following command to generate the iOS and Android project files:
```bash title="Generate project files"
yarn expo prebuild
```
You should now see the `ios` and `android` folders in your project root.
**Step 6: Start the application**
Connect your testing device(s) and run the following command to start the application(s):
**iOS**
```bash title="Run iOS application"
yarn ios --device
```
**Android**
```bash title="Run Android application"
yarn android --device
```
Nice work, your application is now all set to begin using the SDK!
## Configure SDK Tethering [#configure-sdk-tethering]
The iOS and Android Verifier SDKs must be **tethered** to a MATTR VII tenant. On initialization, the SDK registers your app instance with the
tenant and obtains a license, so SDK Tethering must be configured before you initialize the SDK. For
a full explanation of tethering and the capabilities it enables, see [SDK Tethering](/docs/verification/sdks/sdk-tethering).
To enable SDK Tethering, create a Verifier Application on your MATTR VII tenant:
Make a request of the following structure to create an iOS Verifier Application configuration on your MATTR VII tenant:
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "My iOS Verifier Application",
"type": "ios",
"bundleId": "com.yourcompany.verifierapp",
"teamId": "YOUR_APPLE_TEAM_ID",
"appAttest": {
"required": false,
"environment": "development"
}
}
```
* `name`: A unique name to identify this Verifier Application.
* `type`: Must be `ios`.
* `bundleId`: The Bundle ID of your iOS app (must match your Xcode project configuration).
* `teamId`: Your Apple Developer Team ID (must match the Team ID used to sign your app). Get it from your Apple Developer account or from Xcode under **Signing & Capabilities**.
* `appAttest`: App Attest configuration for the iOS verifier application:
* `required`: When `true`, the app instance must provide a valid App Attest attestation during
registration and token renewal. When `false`, the app can fall back to assertion-only
authentication. See [Attestation vs Assertion](/docs/verification/sdks/sdk-tethering#attestation-vs-assertion-fall-back) for more details.
* `environment`: The App Attest environment (`development` or `production`). Apple recommends
using `development` for testing and `production` for distribution builds.
A successful response returns a `201` status code with the created Verifier Application:
```json title="Response"
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c", // [!code focus]
"name": "My iOS Verifier Application",
"type": "ios",
"bundleId": "com.yourcompany.verifierapp",
"teamId": "YOUR_APPLE_TEAM_ID",
"appAttest": {
"required": false,
"environment": "development"
}
}
```
* `id`: A unique identifier for the Verifier Application (generated by the tenant). You must use this value when initializing the SDK so that it can correctly
identify and authenticate your application.
Make a request of the following structure to create an Android Verifier Application configuration on your MATTR VII tenant:
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "My Android Verifier Application",
"type": "android",
"packageName": "com.yourcompany.verifierapp",
"packageSigningCertificateThumbprints": [
"1232584B6F6A892D356899FB9576C5F226A179E6199F2B7A1D837B5C234C5A8E"
],
"keyAttestation": {
"required": false
},
"openid4vpConfiguration": {
"redirectUri": "com.yourcompany.verifierapp://oid4vp-callback"
}
}
```
* `name`: A unique name to identify this Verifier Application.
* `type`: Must be `android`.
* `packageName`: The package name of your Android application.
* `packageSigningCertificateThumbprints`: SHA-256 hex-encoded fingerprints of the signing key
certificates used to sign your APK or app bundle. This ensures the tenant only accepts requests
from known and trusted applications. Refer to
[Android app signing](/docs/verification/android-app-signing) for more information.
* `keyAttestation`: Key Attestation configuration for the Android verifier application:
* `required`: When `true`, the app instance must provide a valid Key Attestation during
registration and token renewal. When `false`, the app can register and renew tokens using
just an authentication assertion. See [Attestation vs Assertion](/docs/verification/sdks/sdk-tethering#attestation-vs-assertion-fall-back) for more details.
* `openid4vpConfiguration.redirectUri`: Required by the create-application endpoint, which needs at
least one of `openid4vpConfiguration` or `dcApiConfiguration`. In-person proximity verification
does not use this redirect, so any valid custom-scheme URI is accepted here.
A successful response returns a `201` status code with the created Verifier Application:
```json title="Response"
{
"id": "a82bfa46-72a0-4cde-b6cb-2a0de7e2f3c4", // [!code focus]
"name": "My Android Verifier Application",
"type": "android",
"packageName": "com.yourcompany.verifierapp",
"packageSigningCertificateThumbprints": [
"1232584B6F6A892D356899FB9576C5F226A179E6199F2B7A1D837B5C234C5A8E"
],
"keyAttestation": {
"required": false
}
}
```
* `id`: A unique identifier for the Verifier Application (generated by the tenant). You must use this value when initializing the SDK so that it can correctly
identify and authenticate your application.
SDK Tethering is currently not required for the React Native Verifier SDK.
## Initialize the SDK [#initialize-the-sdk]
The first capability you will build into your app is to initialize the SDK so that your app can use
SDK functions and classes. To achieve this, we need to import the `MobileCredentialVerifierSDK`
framework and then initialize the `MobileCredentialVerifier` class.
### Step 1: Create the application structure [#step-1-create-the-application-structure]
1. Open the `ContentView` file in your new project and replace any existing code with the
following:
```swift title="ContentView"
import SwiftUI
// Initialize SDK - Step 2.1: Import MobileCredentialVerifierSDK
struct ContentView: View {
@State var viewModel: VerifierViewModel = VerifierViewModel()
var body: some View {
NavigationStack(path: $viewModel.navigationPath) {
VStack {
Button("Scan QR Code") {
viewModel.navigationPath.append(NavigationState.scanQRCode)
}
.padding()
Button("View Response") {
viewModel.navigationPath.append(NavigationState.viewResponse)
}
.padding()
}
.navigationDestination(for: NavigationState.self) { destination in
switch destination {
case .scanQRCode:
codeScannerView
case .viewResponse:
presentationResponseView
}
}
}
.task {
await viewModel.setupCertificates()
}
}
// MARK: Verification Views
var codeScannerView: some View {
// Verify mDocs - Step 2.4: Create QRScannerView
EmptyView()
}
var presentationResponseView: some View {
// Verify mDocs - Step 4.2: Create PresentationResponseView
EmptyView()
}
}
// MARK: VerifierViewModel
@Observable
final class VerifierViewModel {
var navigationPath = NavigationPath()
// Initialize SDK - Step 2.2: Add MobileCredentialVerifier var
// Verify mDocs - Step 1.1: Create MobileCredentialRequest instance
// Verify mDocs - Step 1.2: Create receivedDocuments variable
// Initialize SDK - Step 2.3: Initialize the SDK
func setupCertificates() async {
// Setup certificates - Step 2: Add trusted issuer certificates
print("This method will add the trust anchor to the sdk storage")
}
}
// MARK: Proximity Presentation
extension VerifierViewModel {
func setupProximityPresentationSession(_ deviceEngagementString: String) {
// Verify mDocs - Step 3.2: Create setupProximityPresentationSession
print("This method will use qr code string do setup proximity session")
}
func sendDeviceRequest() {
// Verify mDocs - Step 3.3: Create sendDeviceRequest function
print("This method will send preconfigured device request to holder app")
}
}
// Verify mDocs - Step 3.1: Extend VerifierViewModel class
// MARK: - Navigation
enum NavigationState: Hashable {
case scanQRCode
case viewResponse
}
```
This will serve as the basic structure for your application. We will copy and paste
different code snippets into specific locations to achieve the different functionalities.
These locations are indicated by comments that reference both the section and the step.
We recommend copying and pasting the comment text in Xcode search field (e.g.
`// Initialize SDK - Step 2.2: Add MobileCredentialVerifier var`) to easily locate it in the code.
Open the `app/src/main/java/com.example.verifiertutorial/MainActivity.kt` file in your project and
replace any existing code with the following:
```kotlin title="MainActivity.kt"
package com.example.verifiertutorial
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.example.verifiertutorial.ui.theme.VerifierTutorialTheme
import global.mattr.mobilecredential.verifier.dto.MobileCredentialResponse
import global.mattr.mobilecredential.verifier.MobileCredentialVerifier
import global.mattr.mobilecredential.verifier.platformconfig.PlatformConfiguration
import global.mattr.mobilecredential.verifier.exception.VerifierException.FailedToRegisterException
import global.mattr.mobilecredential.verifier.exception.VerifierException.InvalidLicenseException
import kotlinx.coroutines.launch
import java.net.URL
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Initialize SDK - Step 1.1: Initialize the SDK
enableEdgeToEdge()
setContent {
VerifierTutorialTheme {
val navController = rememberNavController()
NavHost(
modifier = Modifier
.fillMaxSize()
.padding(vertical = 72.dp, horizontal = 8.dp),
startDestination = "home",
navController = navController,
) {
composable("home") {
HomeScreen(navController)
}
composable("scanQr") {
// Verify mDocs - Step 1.7: Add "Scan QR Code" screen call
}
composable("viewResponse") {
// Verify mDocs - Step 4.4: Add "View Response" screen call
}
}
}
}
}
}
@Composable
fun HomeScreen(navController: NavController) {
Column(modifier = Modifier.fillMaxWidth()) {
Button(
modifier = Modifier.fillMaxWidth(),
onClick = { navController.navigate("scanQr") }
) {
Text("Scan QR Code")
}
}
}
// Verify mDocs - Step 2.2: Add shared data
```
This will serve as the basic structure for your application. We will copy and
paste different code snippets into specific locations in this codebase to
achieve the different functionalities. These locations are indicated by
comments that reference both the section and the step.
1. Open the `App.tsx` file in your project and replace the existing code with the skeleton structure:
```tsx title="App.tsx"
import {
type MobileCredentialResponse,
addTrustedIssuerCertificates,
createProximityPresentationSession,
getTrustedIssuerCertificates,
initialize,
sendProximityPresentationRequest,
terminateProximityPresentationSession,
} from "@mattrglobal/mobile-credential-verifier-react-native";
// import { QRScannerModal } from "./QRScannerModal";
// import { VerificationResultsModal } from "./VerificationResultsModal";
import { useCameraPermissions } from "expo-camera";
import { StatusBar } from "expo-status-bar";
import { useEffect, useState } from "react";
import { ActivityIndicator, Alert, SafeAreaView, Text, TouchableOpacity, View } from "react-native";
import { styles } from "./styles";
export default function App() {
// State variables for SDK initialization, UI and loading messages
const [isSDKInitialized, setIsSDKInitialized] = useState(false);
const [loadingMessage, setLoadingMessage] = useState(false);
const [verificationResults, setVerificationResults] = useState(null);
// Modal states
const [isScanning, setIsScanning] = useState(false);
const [showVerificationResults, setShowVerificationResults] = useState(false);
const [permission, requestPermission] = useCameraPermissions();
// Initialize SDK - Step 2.1: Initialize the SDK
// Verify mDocs - Step 1.2: Create handleQRCodeDetected function
return (
mDocs Verifier
{loadingMessage ? (
{loadingMessage}
) : (
{/* Verify mDocs - Step 1.5: Create Scan QR Code Button */}
{!isSDKInitialized && SDK not initialized. Please restart the app.}
)}
{/* Verify mDocs - Step 1.4: Use QRScannerModal */}
{/* Verify mDocs - Step 2.3: Use VerificationResultModal */}
);
}
```
This will serve as the basic structure for your application. We will add code to specific locations to achieve the different functionalities. These locations are indicated by comments that reference both the section and the step.
We recommend using your editor's search functionality to locate comments like `// Initialize SDK - Step 1.3: Initialize the SDK` when adding new code.
### Step 2: Initialize the MobileCredentialVerifier class [#step-2-initialize-the-mobilecredentialverifier-class]
1. Add the following code after the
`// Initialize SDK - Step 2.1: Import MobileCredentialVerifierSDK` comment to import
`MobileCredentialVerifierSDK` and gain access to the SDK's capabilities:
```swift title="ContentView"
import MobileCredentialVerifierSDK
```
2. Add the following code after the
`// Initialize SDK - Step 2.2: Add MobileCredentialVerifier var` comment to create a variable
that holds the `mobileCredentialVerifier` instance:
```swift title="ContentView"
var mobileCredentialVerifier: MobileCredentialVerifier
// Holds the asynchronous initialization work so other calls can await it
// before using the SDK (see Step 2.3).
private var initializationTask: Task?
```
3. Add the following code after the `// Initialize SDK - Step 2.3: Initialize the SDK` comment to
assign a shared instance of the class to our `mobileCredentialVerifier` variable and initialize
the SDK:
```swift title="ContentView"
init() {
mobileCredentialVerifier = MobileCredentialVerifier.shared
// Keep a handle to the initialization Task so later SDK calls can await it.
initializationTask = Task {
do {
let platformConfiguration = PlatformConfiguration(
tenantHost: Constants.tenantHost,
applicationId: Constants.applicationId
)
try await mobileCredentialVerifier.initialize(platformConfiguration: platformConfiguration)
} catch let error as MobileCredentialVerifierError {
// Print the underlying reason so registration failures are visible.
// failedToRegister carries the cause (for example an App Attest
// App ID / team mismatch); invalidLicense means no valid license.
print("SDK initialization failed:", error)
throw error
}
}
}
```
SDK Tethering requires a `platformConfiguration`, so `initialize` now takes one and is
asynchronous (called here from a `Task`). `platformConfiguration` contains the following properties, which we will add as constants in the next step:
* `tenantHost`: The URL of your MATTR VII tenant where your Verifier Application is configured.
* `applicationId`: The `id` returned when you
[created the Verifier Application](#configure-sdk-tethering). Network access is required the
first time the SDK initializes (for registration) and when the license is later renewed.
4. Create a new file named `Constants.swift` and add the following, replacing the placeholders with
your own values:
```swift title="Constants.swift"
import Foundation
enum Constants {
static let tenantHost = URL(string: "https://your-tenant.vii.mattr.global")!
static let applicationId = ""
}
```
* `tenantHost`: The URL of your MATTR VII tenant, available in the MATTR Portal under **Platform Management > Tenant**.
* `applicationId`: The `id` returned when you [created the Verifier Application](#configure-sdk-tethering).
5. [Run](https://developer.apple.com/documentation/xcode/running-your-app-in-simulator-or-on-a-device)
the app to ensure it compiles successfully.
Once the app launches you will see a screen with three buttons, each leading to an empty view. In
the following steps, you will implement proximity presentation functionalities into these views.
1. Add the following code after the `// Initialize SDK - Step 1.1: Initialize the SDK` comment to
initialize the SDK (ensure you replace the `tenantHost` and `applicationId` placeholders with your own values):
We recommend leaving the comment text (e.g. `// Initialize SDK - Step 1.1:
Initialize the SDK`) even after you have pasted the code snippet, as it
will later help you to easily locate the step in the code.
```kotlin title="MainActivity.kt"
lifecycleScope.launch {
val platformConfiguration = PlatformConfiguration(
tenantHost = URL("https://your-tenant.vii.mattr.global"),
applicationId = ""
)
try {
MobileCredentialVerifier.initialize(this@MainActivity, platformConfiguration)
// Setup certificates - Step 2.2: Add trusted issuer certificates
} catch (e: FailedToRegisterException) {
// Registration with the MATTR VII tenant failed — check connectivity and configuration
} catch (e: InvalidLicenseException) {
// The SDK license is missing, invalid, or expired
}
}
```
Initializing the SDK requires a `PlatformConfiguration` object with the following properties:
* `tenantHost`: The URL of your MATTR VII tenant where your Verifier Application is configured. You can find this in the MATTR Portal under **Platform Management > Tenant**.
* `applicationId`: The `id` returned when you
[created the Verifier Application](#configure-sdk-tethering). Network access is required the
first time the SDK initializes (for registration) and when the license is later renewed.
2. [Run](https://developer.android.com/studio/run#basic-build-run) the app to make sure it compiles
properly.
1. Add the following code after the `// Initialize SDK - Step 2.1: Initialize the SDK` comment to initialize the SDK:
We recommend leaving the comment text (e.g. `// Initialize SDK - Step 2.1: Initialize the SDK`) even after you have pasted the code snippet, as it will later help you to easily locate the step in the code.
```tsx title="App.tsx"
useEffect(() => {
const initializeSDK = async () => {
try {
setLoadingMessage("Initializing SDK...");
const result = await initialize();
if (result.isErr()) {
console.error("Failed to initialize SDK:", result.error);
Alert.alert("Error", "Failed to initialize the verifier SDK");
return;
}
setIsSDKInitialized(true);
// Setup certificates - Step 3: Register the trusted IACA certificate on first launch
} catch (error) {
console.error("Failed to initialize SDK:", error);
Alert.alert("Error", "Failed to initialize the verifier SDK");
} finally {
setLoadingMessage(false);
}
};
initializeSDK();
}, []);
```
2. Run the app.
## Setup certificates [#setup-certificates]
Once the SDK is initialized, the next step is to add a trusted issuer certificate.
Every mDoc is signed using a certificate chain, also known as a [chain of trust](/docs/concepts/chain-of-trust). To verify a presented mDoc, your application must confirm that this chain leads back to a trusted root certificate, called an [IACA](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca).
To do this, your application must provide the SDK with the IACA certificates for every issuer it should trust. In this tutorial, you will add the IACA certificate for the MATTR Labs test issuer, which was used to issue the credential you will verify.
1. Create a new file called `IACAs.swift` and add the following code:
```swift title="IACAs.swift"
import Foundation
enum IACAs {
static let mattrLabs =
"""
-----BEGIN CERTIFICATE-----
MIICYzCCAgmgAwIBAgIKXhjLoCkLWBxREDAKBggqhkjOPQQDAjA4MQswCQYDVQQG
EwJBVTEpMCcGA1UEAwwgbW9udGNsaWZmLWRtdi5tYXR0cmxhYnMuY29tIElBQ0Ew
HhcNMjQwMTE4MjMxNDE4WhcNMzQwMTE1MjMxNDE4WjA4MQswCQYDVQQGEwJBVTEp
MCcGA1UEAwwgbW9udGNsaWZmLWRtdi5tYXR0cmxhYnMuY29tIElBQ0EwWTATBgcq
hkjOPQIBBggqhkjOPQMBBwNCAASBnqobOh8baMW7mpSZaQMawj6wgM5e5nPd6HXp
dB8eUVPlCMKribQ7XiiLU96rib/yQLH2k1CUeZmEjxoEi42xo4H6MIH3MBIGA1Ud
EwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRFZwEOI9yq
232NG+OzNQzFKa/LxDAuBgNVHRIEJzAlhiNodHRwczovL21vbnRjbGlmZi1kbXYu
bWF0dHJsYWJzLmNvbTCBgQYDVR0fBHoweDB2oHSgcoZwaHR0cHM6Ly9tb250Y2xp
ZmYtZG12LnZpaS5hdTAxLm1hdHRyLmdsb2JhbC92Mi9jcmVkZW50aWFscy9tb2Jp
bGUvaWFjYXMvMjk0YmExYmMtOTFhMS00MjJmLThhMTctY2IwODU0NWY0ODYwL2Ny
bDAKBggqhkjOPQQDAgNIADBFAiAlZYQP95lGzVJfCykhcpCzpQ2LWE/AbjTGkcGI
SNsu7gIhAJfP54a2hXz4YiQN4qJERlORjyL1Ru9M0/dtQppohFm6
-----END CERTIFICATE-----
""".trimmingCharacters(in: .whitespacesAndNewlines)
}
```
This file contains the root certificate of the MATTR Labs test issuer.
2. Return to the `ContentView.swift` file and replace the `print` statement under the comment `// Setup certificates - Step 2: Add trusted issuer certificates` with the following:
```swift title="ContentView.swift"
do {
// Wait for initialization to finish before using the SDK. This returns
// immediately once initialize has completed, and rethrows if it failed.
try await initializationTask?.value
_ = try await mobileCredentialVerifier.addTrustedIssuerCertificates(certificates: [IACAs.mattrLabs])
} catch {
print("Failed to add trusted issuer certificate:", error)
}
```
This function will be called as soon as the app view appears and the certificate will be added to the app.
1. Create a new file called `Iacas.kt` and add the following code:
```kotlin title="Iacas.kt"
package com.example.verifiertutorial
object Iacas {
val mattrLabs = """
-----BEGIN CERTIFICATE-----
MIICYzCCAgmgAwIBAgIKXhjLoCkLWBxREDAKBggqhkjOPQQDAjA4MQswCQYDVQQG
EwJBVTEpMCcGA1UEAwwgbW9udGNsaWZmLWRtdi5tYXR0cmxhYnMuY29tIElBQ0Ew
HhcNMjQwMTE4MjMxNDE4WhcNMzQwMTE1MjMxNDE4WjA4MQswCQYDVQQGEwJBVTEp
MCcGA1UEAwwgbW9udGNsaWZmLWRtdi5tYXR0cmxhYnMuY29tIElBQ0EwWTATBgcq
hkjOPQIBBggqhkjOPQMBBwNCAASBnqobOh8baMW7mpSZaQMawj6wgM5e5nPd6HXp
dB8eUVPlCMKribQ7XiiLU96rib/yQLH2k1CUeZmEjxoEi42xo4H6MIH3MBIGA1Ud
EwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRFZwEOI9yq
232NG+OzNQzFKa/LxDAuBgNVHRIEJzAlhiNodHRwczovL21vbnRjbGlmZi1kbXYu
bWF0dHJsYWJzLmNvbTCBgQYDVR0fBHoweDB2oHSgcoZwaHR0cHM6Ly9tb250Y2xp
ZmYtZG12LnZpaS5hdTAxLm1hdHRyLmdsb2JhbC92Mi9jcmVkZW50aWFscy9tb2Jp
bGUvaWFjYXMvMjk0YmExYmMtOTFhMS00MjJmLThhMTctY2IwODU0NWY0ODYwL2Ny
bDAKBggqhkjOPQQDAgNIADBFAiAlZYQP95lGzVJfCykhcpCzpQ2LWE/AbjTGkcGI
SNsu7gIhAJfP54a2hXz4YiQN4qJERlORjyL1Ru9M0/dtQppohFm6
-----END CERTIFICATE-----
""".trimIndent()
}
```
This file contains the root certificate of the MATTR Labs test issuer.
2. Return to the `MainActivity.kt` file and add the following code after the `// Setup certificates - Step 2.2: Add trusted issuer certificates` comment to add the MATTR Labs test issuer certificate to the SDK:
```kotlin title="MainActivity.kt"
MobileCredentialVerifier.addTrustedIssuerCertificates(listOf(Iacas.mattrLabs))
```
1. In your project root, create a new file called `certificates.ts` and add the following code:
```ts title="certificates.ts"
// MATTR Labs test issuer (montcliff-dmv.mattrlabs.com) IACA
export const MONTCLIFF_DMV_IACA = `MIICYzCCAgmgAwIBAgIKXhjLoCkLWBxREDAKBggqhkjOPQQDAjA4MQswCQYDVQQG
EwJBVTEpMCcGA1UEAwwgbW9udGNsaWZmLWRtdi5tYXR0cmxhYnMuY29tIElBQ0Ew
HhcNMjQwMTE4MjMxNDE4WhcNMzQwMTE1MjMxNDE4WjA4MQswCQYDVQQGEwJBVTEp
MCcGA1UEAwwgbW9udGNsaWZmLWRtdi5tYXR0cmxhYnMuY29tIElBQ0EwWTATBgcq
hkjOPQIBBggqhkjOPQMBBwNCAASBnqobOh8baMW7mpSZaQMawj6wgM5e5nPd6HXp
dB8eUVPlCMKribQ7XiiLU96rib/yQLH2k1CUeZmEjxoEi42xo4H6MIH3MBIGA1Ud
EwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRFZwEOI9yq
232NG+OzNQzFKa/LxDAuBgNVHRIEJzAlhiNodHRwczovL21vbnRjbGlmZi1kbXYu
bWF0dHJsYWJzLmNvbTCBgQYDVR0fBHoweDB2oHSgcoZwaHR0cHM6Ly9tb250Y2xp
ZmYtZG12LnZpaS5hdTAxLm1hdHRyLmdsb2JhbC92Mi9jcmVkZW50aWFscy9tb2Jp
bGUvaWFjYXMvMjk0YmExYmMtOTFhMS00MjJmLThhMTctY2IwODU0NWY0ODYwL2Ny
bDAKBggqhkjOPQQDAgNIADBFAiAlZYQP95lGzVJfCykhcpCzpQ2LWE/AbjTGkcGI
SNsu7gIhAJfP54a2hXz4YiQN4qJERlORjyL1Ru9M0/dtQppohFm6`;
```
This file contains the root certificate of the MATTR Labs test issuer.
2. Back in `App.tsx`, import the certificate alongside your existing imports:
```tsx title="App.tsx"
import { MONTCLIFF_DMV_IACA } from "./certificates";
```
3. Add the following code after the `// Setup certificates - Step 3: Register the trusted IACA certificate on first launch` comment to register the MATTR Labs test issuer certificate with the SDK the first time the app runs:
```tsx title="App.tsx"
setLoadingMessage("Loading certificates...");
const certificates = await getTrustedIssuerCertificates();
if (certificates.length === 0) {
await addTrustedIssuerCertificates([MONTCLIFF_DMV_IACA]);
}
```
The SDK persists trusted certificates between launches, so this check ensures the sample certificate is only added once.
## Verify mDocs [#verify-mdocs]
In this part we will build the components that enable a verifier app to verify an mDoc presented via
a proximity workflow as per [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html):
To achieve this, your application must be able to:
1. Create a presentation request that defines the information required for verification.
2. Scan and process a QR code presented by a wallet application. Your application must retrieve the information from that QR code and use it to establish a secure connection between the verifier and holder devices.
3. Your verifier application then uses this secure connection to send a presentation request to which the holder wallet application responds with a presentation response.
4. Finally, the SDK verifies any mDocs included in the response, stores the verification results in a variable and makes them available to your application to display.
Your application will use the SDK's `createProximityPresentationSession` function
that takes a string retrieved from the QR code and uses it to establish a proximity presentation
session with the wallet application and initiate the presentation workflow.
This function takes a `listener` argument of type `ProximityPresentationSessionListener` delegate,
which will receive proximity presentation session events.
**Step 1: Create a presentation request**
As a verifier, you can select what information you request for verification. Your application
implements this by creating a
[MobileCredentialRequest](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/mobilecredentialrequest)
instance to define the required information, and a new variable to hold the response from the wallet
application.
1. Open the `ContentView` file and add the following code under the
`// Verify mDocs - Step 1.1: Create MobileCredentialRequest instance` comment to define what
information to request from the wallet application user:
```swift title="ContentView"
let mobileCredentialRequest = MobileCredentialRequest(
docType: "org.iso.18013.5.1.mDL",
namespaces: [
"org.iso.18013.5.1": [
"family_name": false,
"given_name": false,
"birth_date": false
]
]
)
```
This object details:
* The requested credential type (e.g. `org.iso.18013.5.1.mDL`).
* The claims required for verification (e.g. `family_name`).
* The requested namespace (e.g. `org.iso.18013.5.1`).
* Whether or not the verifier intends to persist the claim value (`true`/`false`).
For the verification to be successful, the presented credential must include the referenced
claim against the specific namespace defined in the request. Our example requests the
`birth_date` under the `org.iso.18013.5.1` namespace. If a wallet responds to this request with
a credential that includes a `birth_date` but rather under the `org.iso.18013.5.1.US` namespace,
the claim will not be verified.
To simplify the tutorial, this is a hardcoded request. However, once you are
comfortable with the basic functionalities you can create a UI in your
verifier application that enables the user to create different requests on the
fly by selecting different claims to include. Check out our [GO Verify
app](/docs/verification/go-verify/getting-started) to see this in action.
2. Add the following code under the `Verify mDocs - Step 1.2: Create receivedDocuments variable`
comment to create a new `receivedDocuments` variable that will hold the response from the wallet
application:
```swift title="ContentView"
var receivedDocuments: [MobileCredentialPresentation] = []
```
Your application now has an existing credential request to share, and a variable to hold any
incoming responses. In the next step we will build the capabilities to send this request and handle
the response.
**Step 2: Scan and process a QR code**
As defined in ISO/IEC 18130-5:2021, a
[proximity presentation workflow](/docs/verification/in-person-overview) is always initiated by the
holder (wallet application user), who must create a QR code for the verifier to scan in order to
initiate the [device engagement phase](/docs/verification/in-person-overview#engagement-phase).
This means that your verifier application must be able to scan and process this QR code. For ease of
implementation, we will use a third party framework to achieve this.
1. Add [camera usage permissions](https://help.apple.com/xcode/mac/current/#/dev37c2f42ff) to the
app target:
2. Add the [CodeScanner](https://github.com/twostraws/CodeScanner) library via
[Swift Package Manager](https://help.apple.com/xcode/mac/current/#/devb83d64851).
3. [Create a new swift file](https://help.apple.com/xcode/mac/current/#/dev81ce1d383) named
`QRScannerView` and add the following code into it to implement the QR scanning capability:
```swift title="QRScannerView"
import SwiftUI
import CodeScanner
import AVFoundation
struct QRScannerView: View {
private let completionHandler: (String) -> Void
init(completion: @escaping (String) -> Void) {
completionHandler = completion
}
var body: some View {
CodeScannerView(codeTypes: [.qr]) { result in
switch result {
case .failure(let error):
print(error.localizedDescription)
case .success(let result):
print(result.string)
completionHandler(result.string)
}
}
}
}
```
4. Back in the `ContentView` file, replace the `EmptyView()` under the
`// Verify mDocs - Step 2.4: Create QRScannerView` comment with the following code to create a
new app view that the user will use to scan a QR code:
```swift title="ContentView"
QRScannerView(
completion: { string in
viewModel.setupProximityPresentationSession(string)
}
)
```
5. [Run](https://developer.apple.com/documentation/xcode/running-your-app-in-simulator-or-on-a-device)
the app and select the **Scan QR Code** button. You should be navigated to the new
`QRScannerView` where you can use the camera to scan a QR code.
Next we will build the logic that handles this QR code to establish a secure connection with the
wallet application.
**Step 3: Exchange presentation request and response**
1. Add the following code under the `Verify mDocs - Step 3.1: Extend VerifierViewModel class` to
extend the `VerifierViewModel` class with the
[`ProximityPresentationSessionListener`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/proximitypresentationsessionlistener)
protocol:
```swift title="ContentView"
extension VerifierViewModel: ProximityPresentationSessionListener {
public func onEstablished() {
sendDeviceRequest()
}
// Session-creation failures (Bluetooth permission, transport setup,
// unsupported curve, and so on) are delivered here, not to onTerminated.
// onError has an empty default implementation, so without this method
// those failures would be silent.
public func onError(error: (any Error)?) {
print("Proximity session error:", error?.localizedDescription ?? "unknown")
}
public func onTerminated(error: (any Error)?) {
print("Session terminated:", error?.localizedDescription ?? "none")
}
}
```
Now, as soon as a connection is established, the app will send a device request. You will
implement the functionality of `sendDeviceRequest()` in `VerifierViewModel` later in the
tutorial. If a session cannot be created, `onError` reports the reason.
2. Replace the `print` statement under the
`// Verify mDocs - Step 3.2: Create setupProximityPresentationSession` comment with the
following code to call the SDK's
[`createProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/mobilecredentialverifier/createproximitypresentationsession\(encodeddeviceengagementstring:listener:\))
function, passing a device engagement string (retrieved from a QR code) and `self` as a listener
to create a proximity presentation session:
```swift title="ContentView"
mobileCredentialVerifier.createProximityPresentationSession(encodedDeviceEngagementString: deviceEngagementString, listener: self)
```
3. Replace the `print` statement under the
`// Verify mDocs - Step 3.3: Create sendDeviceRequest function` comment with following code to
implement the logic to send a device request:
```swift title="ContentView"
Task { @MainActor in
receivedDocuments = []
do {
// Navigate to response screen
navigationPath.append(NavigationState.viewResponse)
// Request mDocs
let deviceResponse = try await mobileCredentialVerifier.sendProximityPresentationRequest(
request: [mobileCredentialRequest]
)
// Assign new values from the response
receivedDocuments = deviceResponse.credentials
// Terminate session after response is received (optional)
await mobileCredentialVerifier.terminateProximityPresentationSession()
} catch {
print(error)
receivedDocuments = []
}
}
```
This function now implements the following logic:
1. Navigate to the `viewResponse` screen.
2. Send a proximity presentation request using the SDK's
[`requestMobileCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/proximitypresentationsession/requestmobilecredentials\(request:\))
function.
3. Store the wallet response in the `deviceResponse` variable. This includes the verification
results of any credentials included in the response.
4. Store the verification results in the `receivedDocuments` variable.
5. Terminate the presentation session once the response is received.
**Step 4: Display verification results**
1. Create a new file named `DocumentView` and add the following code to display available
verification results:
```swift title="DocumentView"
import MobileCredentialVerifierSDK
import SwiftUI
struct DocumentView: View {
var viewModel: DocumentViewModel
var body: some View {
VStack(alignment: .leading, spacing: 10) {
Text(viewModel.docType)
.font(.title)
.fontWeight(.bold)
.padding(.bottom, 5)
Text(viewModel.verificationResult)
.font(.title)
.fontWeight(.bold)
.foregroundStyle(viewModel.verificationFailedReason == nil ? .green : .red)
.padding(.bottom, 5)
if let verificationFailedReason = viewModel.verificationFailedReason {
Text(verificationFailedReason)
.font(.title3)
.fontWeight(.bold)
.foregroundStyle(.red)
.padding(.bottom, 5)
}
ForEach(viewModel.namespacesAndClaims.keys.sorted(), id: \.self) { key in
VStack(alignment: .leading, spacing: 5) {
Text(key)
.font(.headline)
.padding(.vertical, 5)
.padding(.horizontal, 10)
.background(Color.gray.opacity(0.2))
.cornerRadius(5)
ForEach(viewModel.namespacesAndClaims[key]!.keys.sorted(), id: \.self) { claim in
HStack {
Text(claim)
.fontWeight(.semibold)
Spacer()
Text(viewModel.namespacesAndClaims[key]![claim]! ?? "")
.fontWeight(.regular)
}
.padding(.vertical, 5)
.padding(.horizontal, 10)
.background(Color.white)
.cornerRadius(5)
.shadow(radius: 1)
}
}
.padding(.vertical, 5)
}
if !viewModel.claimErrors.isEmpty {
Text("Failed Claims:")
.font(.headline)
.padding(.vertical, 5)
ForEach(viewModel.claimErrors.keys.sorted(), id: \.self) { key in
VStack(alignment: .leading, spacing: 5) {
Text(key)
.font(.headline)
.padding(.vertical, 5)
.padding(.horizontal, 10)
.background(Color.gray.opacity(0.2))
.cornerRadius(5)
ForEach(viewModel.claimErrors[key]!.keys.sorted(), id: \.self) { claim in
HStack {
Text(claim)
.fontWeight(.semibold)
Spacer()
Text(viewModel.claimErrors[key]![claim]! ?? "")
.fontWeight(.regular)
}
.padding(.vertical, 5)
.padding(.horizontal, 10)
.background(Color.white)
.cornerRadius(5)
.shadow(radius: 1)
}
}
.padding(.vertical, 5)
}
}
}
.padding()
.background(RoundedRectangle(cornerRadius: 10).fill(Color.white).shadow(radius: 5))
.padding(.horizontal)
}
}
// MARK: DocumentViewModel
@Observable
class DocumentViewModel {
let docType: String
let namespacesAndClaims: [String: [String: String?]]
let claimErrors: [String: [String: String?]]
let verificationResult: String
let verificationFailedReason: String?
init(from presentation: MobileCredentialPresentation) {
self.docType = presentation.docType
self.verificationResult = presentation.verificationResult.verified ? "Verified" : "Invalid"
self.verificationFailedReason = presentation.verificationResult.failureType?.rawValue
self.namespacesAndClaims = presentation.claims?.reduce(into: [String: [String: String]]()) { result, outerElement in
let (outerKey, innerDict) = outerElement
result[outerKey] = innerDict.mapValues { $0.textRepresentation }
} ?? [:]
self.claimErrors = presentation.claimErrors?.reduce(into: [String: [String: String]]()) { result, outerElement in
let (outerKey, innerDict) = outerElement
result[outerKey] = innerDict.mapValues { "\($0)" }
} ?? [:]
}
}
// MARK: Helper
extension MobileCredentialElementValue {
var textRepresentation: String {
switch self {
case .bool(let bool):
return "\(bool)"
case .string(let string):
return string
case .int(let int):
return "\(int)"
case .unsigned(let uInt):
return "\(uInt)"
case .float(let float):
return "\(float)"
case .double(let double):
return "\(double)"
case let .date(date):
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .none
return dateFormatter.string(from: date)
case let .dateTime(date):
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .short
return dateFormatter.string(from: date)
case .data(let data):
return "Data \(data.count) bytes"
case .map(let dictionary):
let result = dictionary.mapValues { value in
value.textRepresentation
}
return "\(result)"
case .array(let array):
return array.reduce("") { partialResult, element in
partialResult + element.textRepresentation
}
.appending("")
@unknown default:
return "Unknown type"
}
}
}
```
The `DocumentView` file comprises the following elements:
* `DocumentView` : Basic UI layout for viewing received documents and verification results.
* `DocumentViewModel` : This class takes `MobileCredentialPresentation` and converts its
elements into strings to display in the `DocumentView`.
* Extension of `MobileCredentialElementValue` which converts the values of received claims into
a human-readable format.
2. Return to the `ContentView` file and replace the `EmptyView()` under the
`// Verify mDocs - Step 4.2: Create PresentationResponseView` comment with the following code to
display the `DocumentView` view when verification results are available:
```swift title="ContentView"
ZStack {
if viewModel.receivedDocuments.isEmpty {
VStack(spacing: 40) {
Text("Waiting for response...")
.font(.title)
ProgressView()
.progressViewStyle(.circular)
.scaleEffect(2)
}
} else {
ScrollView {
ForEach(viewModel.receivedDocuments, id: \.docType) { doc in
DocumentView(viewModel: DocumentViewModel(from: doc))
.padding(10)
}
}
}
}
```
**Step 1: Create a screen for scanning the credential offer**
As defined in ISO/IEC 18130-5:2021, a
[proximity presentation workflow](/docs/verification/in-person-overview) is always initiated by the
holder (wallet application user), who must create a QR code for the verifier to scan in order to
initiate the [device engagement phase](/docs/verification/in-person-overview#engagement-phase).
This means that your verifier application must be able to scan and process this QR code. For ease of
implementation, we will use a third party framework to achieve this.
1. Add dependencies to your `app/build.gradle.kts`:
```kotlin title="app/build.gradle.kts"
implementation("com.google.accompanist:accompanist-permissions:0.36.0")
implementation("com.journeyapps:zxing-android-embedded:4.3.0")
```
2. [Sync project with Gradle files](https://developer.android.com/build#sync-files).
3. In your package, create a new file called `ScanQrScreen.kt` and add the following code:
```kotlin title="ScanQrScreen.kt"
import android.app.Activity
import android.Manifest
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import androidx.navigation.NavController
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import com.journeyapps.barcodescanner.BarcodeCallback
import com.journeyapps.barcodescanner.DecoratedBarcodeView
import global.mattr.mobilecredential.verifier.deviceretrieval.devicerequest.DataElements
import global.mattr.mobilecredential.verifier.deviceretrieval.devicerequest.NameSpaces
import global.mattr.mobilecredential.verifier.dto.MobileCredentialRequest
import global.mattr.mobilecredential.verifier.dto.MobileCredentialResponse
import global.mattr.mobilecredential.verifier.MobileCredentialVerifier
import global.mattr.mobilecredential.verifier.ProximityPresentationSessionListener
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun ScanQrScreen(activity: Activity, navController: NavController) {
// Verify mDocs - Step 1.6: Add permission request logic
}
// Verify mDocs - Step 1.5: Add screen content
// Verify mDocs - Step 1.4: Add QR scan callback
// Verify mDocs - Step 3.1: Create session listener
// Verify mDocs - Step 2.1: Create a sample request
```
4. In the `ScanQrScreen.kt` file, add the following code under the
`// Verify mDocs - Step 1.4: Add QR scan callback` comment to define a callback that is called
when the QR code was successfully scanned (we will implement the callback logic at a later
stage):
```kotlin title="ScanQrScreen.kt"
private fun onQrScanned(
activity: Activity,
deviceEngagement: String,
coroutineScope: CoroutineScope,
navController: NavController
) {
coroutineScope.launch {
// Verify mDocs - Step 3.2: Create session
// Verify mDocs - Step 4.1: Handle response
}
}
```
5. Add the following code under the `// Verify mDocs - Step 1.5: Add screen content` comment to
define the main UI of the screen:
```kotlin title="ScanQrScreen.kt"
@Composable
private fun Content(activity: Activity, navController: NavController) {
val context = LocalContext.current
val barcodeView = remember { DecoratedBarcodeView(context) }
val coroutineScope = rememberCoroutineScope()
var isQrScanned by remember { mutableStateOf(false) }
val barcodeCallback = remember {
BarcodeCallback { result ->
onQrScanned(activity, result.text, coroutineScope, navController)
barcodeView.pause()
isQrScanned = true
}
}
DisposableEffect(Unit) {
barcodeView.decodeContinuous(barcodeCallback)
barcodeView.resume()
onDispose { barcodeView.pause() }
}
if (!isQrScanned) {
AndroidView(factory = { barcodeView }, modifier = Modifier.fillMaxSize())
} else {
Box(Modifier.fillMaxSize()) {
CircularProgressIndicator(Modifier.align(Alignment.Center))
}
}
}
```
Please have a quick look at the code. The screen will show a QR scanning view. As soon as the QR
code is captured, it shows a progress spinning wheel, and calls `onQrScanned` function.
6. Add the following code under the `// Verify mDocs - Step 1.6: Add permission request logic`
comment to define a basic logic for requesting the camera access permission at runtime:
```kotlin title="ScanQrScreen.kt"
val cameraPermissionState = rememberPermissionState(Manifest.permission.CAMERA)
val requestPermissionLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) {}
LaunchedEffect(cameraPermissionState) {
if (!cameraPermissionState.status.isGranted) {
requestPermissionLauncher.launch(Manifest.permission.CAMERA)
}
}
if (cameraPermissionState.status.isGranted) Content(activity, navController)
```
7. Back in the `MainActivity` file, add the following code under the
`// Verify mDocs - Step 1.7: Add "Scan QR" screen call` to connect the created screen to the
navigation graph:
```kotlin title="MainActivity.kt"
ScanQrScreen(this@MainActivity, navController)
```
8. [Run](https://developer.android.com/studio/run#basic-build-run) the app and select the **Scan QR
Code** button. You should be navigated to the new `QRScannerView` where you can use the camera to
scan a QR code.
Now we will build the logic that handles this QR code to establish a secure connection with the
wallet application.
**Step 2: Create a presentation request**
As a verifier, you can select what information you request for verification. Our application
implements this by creating a
[MobileCredentialRequest](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/m-docs%20-verifier%20-s-d-k/global.mattr.mobilecredential.verifier.dto/-mobile-credential-request/index.html)
instance to define the required information, and a new variable to hold the response from the wallet
application.
1. In the `ScanQrScreen.kt` file, add the following code under the
`// Verify mDocs - Step 2.1: Create a sample request` comment to define what information to
request from the holder's application:
```kotlin title="ScanQrScreen.kt"
private val sampleMdocRequest = MobileCredentialRequest(
docType = "org.iso.18013.5.1.mDL",
namespaces = NameSpaces(
mapOf(
"org.iso.18013.5.1" to DataElements(
listOf("given_name", "family_name", "birth_date").associateWith { false }
)
)
)
)
```
This object details:
* The requested credential type (e.g. `org.iso.18013.5.1.mDL`).
* The claims required for verification (e.g. `given_name`).
* The requested namespace (e.g. `org.iso.18013.5.1`).
* Whether or not the verifier intends to persist the claim value (`true`/`false`).
For the verification to be successful, the presented credential must include the referenced
claim against the specific namespace defined in the request. Our example requests the
`birth_date` under the `org.iso.18013.5.1` namespace. If a wallet responds to this request with
a credential that includes a `birth_date` but rather under the `org.iso.18013.5.1.US` namespace,
the claim will not be verified.
To simplify the tutorial, this is a hardcoded request. However, once you are
comfortable with the basic functionalities you can create a UI in your
verifier application that enables the user to create different requests on the
fly by selecting different claims to include. See our [GO Verify
app](/docs/verification/go-verify/getting-started) as an example.
2. Back in the `MainActivity.kt` file, add the following code under the
`// Verify mDocs - Step 2.2: Add shared data` comment to create a new `credentialResponse`
variable that will hold the response from the holder's application:
```kotlin title="MainActivity.kt"
object SharedData {
var credentialResponse: MobileCredentialResponse? = null
}
```
Now your application has an existing request to share, and a variable to hold any incoming
responses. We can now proceed to build the capabilities to send the request and handle the response.
**Step 3: Exchange presentation request and response**
1. In `ScanQrScreen.kt`, add the following code under the
`// Verify mDocs - Step 3.1: Create session listener` to define a listener that will react to the
proximity presentation session lifecycle events:
```kotlin title="ScanQrScreen.kt"
private class SessionListener(
private val coroutineScope: CoroutineScope,
private val continuation: Continuation
) : ProximityPresentationSessionListener {
override fun onEstablished() {
coroutineScope.launch {
// Verify mDocs - Step 3.3: Request credentials
}
}
override fun onTerminated(error: Throwable?) {
/* no-op */
}
override fun onError(error: Throwable?) {
error?.let { continuation.resumeWithException(it) }
}
}
```
2. Add the following code under the `// Verify mDocs - Step 3.2: Create session` to create proximity
presentation session and register a session listener:
```kotlin title="ScanQrScreen.kt"
SharedData.credentialResponse = try {
suspendCancellableCoroutine { continuation: Continuation ->
val sessionListener = SessionListener(coroutineScope, continuation)
MobileCredentialVerifier
.createProximityPresentationSession(activity, deviceEngagement, sessionListener)
}
} catch (e: Exception) {
Toast.makeText(activity, "Failed to request credentials", Toast.LENGTH_SHORT).show()
null
}
```
We pass `Continuation` to the listener. It will be resumed either with
`MobileCredentialResponse` if the whole presentation flow is successful, or with an exception if
there was an issue during any stage of the credentials presentation.
3. Add the following code under the `// Verify mDocs - Step 3.3: Request credentials` to request the
mobile credentials:
```kotlin title="ScanQrScreen.kt"
try {
val response = MobileCredentialVerifier.sendProximityPresentationRequest(
listOf(sampleMdocRequest)
)
MobileCredentialVerifier.terminateProximityPresentationSession()
continuation.resume(response)
} catch (e: Exception) {
continuation.resumeWithException(e)
}
```
The resulting code:
1. Establishes a secure connection with the wallet application by calling
[`createProximityPresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/m-docs%20-verifier%20-s-d-k/global.mattr.mobilecredential.verifier/-mobile-credential-verifier/create-proximity-presentation-session.html?query=fun%20createProximityPresentationSession\(activity:%20Activity,%20qrEncodedDeviceEngagementString:%20String,%20listener:%20ProximityPresentationSessionListener\)).
2. Calls
[`sendProximityPresentationRequest`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/m-docs%20-verifier%20-s-d-k/global.mattr.mobilecredential.verifier/-mobile-credential-verifier/send-proximity-presentation-request.html)
function as soon as the session is established. The function accepts a list of
[`MobileCredentialRequest`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/m-docs%20-verifier%20-s-d-k/global.mattr.mobilecredential.verifier.dto/-mobile-credential-request/index.html?query=data%20class%20MobileCredentialRequest\(val%20docType:%20DocType,%20val%20namespaces:%20NameSpaces\)), sends the requests to the wallet application, receives a response from the wallet application,
and verifies any mDocs included in the response.
3. Stores the response in the `SharedData.credentialResponse` value.
4. Handles the exceptions, if they were thrown from the above calls. An exception can be thrown if,
for example, the Bluetooth connection between the Holder and Verifier devices was interrupted
during the session.
Now that we have the verification results stored, you can implement different business logics to
handle the results.
For this tutorial, we will display these results to the verifier app user, individually indicating
the verification status of each claim included in the request.
**Step 4: Display verification results**
1. Add the following code under the `// Verify mDocs - Step 4.1: Handle response` to navigate the
user to the response screen, where they can see the retrieved credentials, if the retrieval was
successful:
```kotlin title="ScanQrScreen.kt"
SharedData.credentialResponse?.let {
navController.navigate("viewResponse") { popUpTo("home") }
}
```
2. Create a new file named `ViewResponseScreen.kt` that will be used to display the response to the
verifier application user.
3. Copy and paste the following code into the new file:
```kotlin title="ViewResponseScreen.kt"
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import global.mattr.mobilecredential.verifier.deviceretrieval.deviceresponse.DataElementIdentifier
import global.mattr.mobilecredential.verifier.deviceretrieval.deviceresponse.NameSpace
import global.mattr.mobilecredential.verifier.dto.MobileCredentialElement
@Composable
fun ViewResponseScreen() {
// Verify mDocs - Step 4.5: Define content
}
// Verify mDocs - Step 4.8: Display claims
// Verify mDocs - Step 4.7: Map a claim or an error to string
```
4. Back in the `MainActivity.kt` file, add the following code under the
`// Verify mDocs - Step 4.4: Add "View Response" screen call` comment to connect the created
composable to the navigation graph:
```kotlin title="MainActivity.kt"
ViewResponseScreen()
```
5. Return to the `ViewResponseScreen.kt` screen and add the following code under the
`// Verify mDocs - Step 4.5: Define content` comment to define the basic UI for displaying the
response details to the verifier application user:
```kotlin title="ViewResponseScreen.kt"
val credential = SharedData.credentialResponse?.credentials?.firstOrNull()
if (credential == null || SharedData.credentialResponse?.credentialErrors?.isNotEmpty() == true) {
// Verify mDocs - Step 4.6: Show error
} else {
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
// Verify mDocs - Step 4.10: Show credential verification status
// Verify mDocs - Step 4.9: Show retrieved claims and errors
}
}
```
While our SDK
[allows](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/m-docs%20-verifier%20-s-d-k/global.mattr.mobilecredential.verifier/-mobile-credential-verifier/send-proximity-presentation-request.html)
to request multiple document types (and thus, multiple credentials) at the
same time, for the tutorial simplicity we requested only one document type,
and expect to see only one mobile credential as the response. Because of that,
we take and handle only the first element from the retrieved credentials list.
6. Add the following code under the `// Verify mDocs - Step 4.6: Show error` comment to show an
error message in case the response is empty or if there were major errors during the response
retrieval:
```kotlin title="ViewResponseScreen.kt"
Box(Modifier.fillMaxSize()) {
Text("There were errors while receiving the response", Modifier.align(Alignment.Center))
}
```
7. Add the following code under the `// Verify mDocs - Step 4.7: Map a claim or an error to string`
comment to map the received claim value (or a claim error) to a string:
```kotlin title="ViewResponseScreen.kt"
private fun Any.claimToUiString() = when (this) {
is MobileCredentialElement -> {
when (this) {
is MobileCredentialElement.ArrayElement, is MobileCredentialElement.DataElement,
is MobileCredentialElement.MapElement -> this::class.simpleName ?: "Unknown element"
else -> value.toString()
}
}
else -> "Not returned"
}
```
*Claim error* here means that the presentation session has completed
successfully, without interruption, and the mobile credentials were received
and verified, but some of the claim values were not sent to the verifier. Refer to the [Handling verification results](/docs/verification/in-person-guides/handling-verification-results) guide for more information.
8. Add the following code under the `// Verify mDocs - Step 4.8: Display claims` comment to create a
function that displays the retrieved and failed claims to the verifier application user:
```kotlin title="ViewResponseScreen.kt"
@Composable
private fun ColumnScope.Claims(
title: String,
claims: Map>?
) {
Text(
title,
modifier = Modifier
.fillMaxWidth()
.align(Alignment.CenterHorizontally),
style = MaterialTheme.typography.titleLarge
)
claims?.forEach { (namespace, claims) ->
Card {
Column(Modifier.padding(6.dp)) {
Text(
namespace,
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(vertical = 4.dp)
)
claims.forEach { (name, value) ->
Row {
Text(name,
Modifier
.weight(1f)
.padding(end = 4.dp))
Text(value.claimToUiString(), overflow = TextOverflow.Ellipsis)
}
}
}
}
} ?: Text("Nothing here")
}
```
9. Add the following code under the `// Verify mDocs - Step 4.9: Show retrieved claims and errors`
comment to create the UI for showing the retrieved and failed claims on the screen:
```kotlin title="ViewResponseScreen.kt"
Claims("Received claims", credential.claims)
Spacer(Modifier.padding(8.dp))
Claims("Failed claims", credential.claimErrors)
```
10. Add the following code under the
`// Verify mDocs - Step 4.10: Show credential verification status` comment to show the overall
verification status:
```kotlin title="ViewResponseScreen.kt"
val statusStyle = MaterialTheme.typography.titleLarge
if (credential.verificationResult.verified) {
Text("Verified", style = statusStyle, color = Color.Green)
} else {
Text("Not verified", style = statusStyle, color = Color.Red)
}
```
**Step 1: Create a component for scanning a QR code presented by the holder**
1. Create a new file called `QRScannerModal.tsx` and add the following code into it to implement the QR
scanning capability:
```tsx title="QRScannerModal.tsx"
import { CameraView } from "expo-camera";
import { useEffect, useRef, useState } from "react";
import { Alert, Modal, SafeAreaView, Text, TouchableOpacity, View } from "react-native";
import { styles } from "./styles";
interface QRScannerModalProps {
visible: boolean;
onClose: () => void;
permission: any;
requestPermission: () => Promise;
onQRCodeDetected: (data: string) => void;
}
export function QRScannerModal({
visible,
onClose,
permission,
requestPermission,
onQRCodeDetected,
}: QRScannerModalProps) {
const [scanned, setScanned] = useState(false);
const [scanningEnabled, setScanningEnabled] = useState(true);
const handlerCalledRef = useRef(false);
const handleBarCodeScanned = ({ data }: { data: string }) => {
if (!scanningEnabled || scanned || handlerCalledRef.current) {
return;
}
// Immediately mark as handled and disable scanning
handlerCalledRef.current = true;
setScanningEnabled(false);
setScanned(true);
console.log(`Scanned barcode with data: ${data}`);
// Check if data starts with "mdoc:"
if (!data || !data.startsWith("mdoc:")) {
Alert.alert(
"Invalid QR Code",
"The QR code must be an mDoc QR code starting with 'mdoc:'. Please scan a valid mDoc QR code.",
[
{
text: "Try Again",
onPress: () => resetScanner(),
},
]
);
return;
}
console.log("Valid mDoc QR code detected:", data);
// Close modal immediately to stop camera
onClose();
// Call handler after modal is closed to prevent camera from firing again
setTimeout(() => {
onQRCodeDetected(data);
}, 300);
};
const resetScanner = () => {
handlerCalledRef.current = false;
setScanned(false);
setScanningEnabled(true);
};
const handleClose = () => {
resetScanner();
onClose();
};
// Reset scanner state when modal becomes visible
useEffect(() => {
if (visible) {
resetScanner();
}
}, [visible]);
if (!visible) return null;
return (
QR Code ScannerClose
{!permission ? (
Camera permissions are still loading
) : !permission.granted ? (
Camera permission is required to scan QR codesRequest Permission
) : (
<>
{!scanned && (
)}
{scanned ? "Processing QR code..." : "Point your camera at a QR code"}
{scanned && (
Scan Again
)}
>
)}
);
}
```
* This component uses the expo-camera package and handles camera permissions through props passed from `App.tsx`.
* It's configured to scan QR codes and validates that the scanned data starts with "mdoc:" prefix.
* The `handleBarCodeScanned` function processes the scanned data and calls the `onQRCodeDetected` callback with the QR code data.
2. Return to your `App.tsx` file and add the following code under the `// Verify mDocs - Step 1.2: Create handleQRCodeDetected function` comment to add a presentation workflow handler. This handler uses the SDK's `createProximityPresentationSession` and `sendProximityPresentationRequest` and methods to establish a session and request a credential:
```tsx title="App.tsx"
const handleQRCodeDetected = async (qrData: string) => {
try {
setLoadingMessage("Establishing secure connection...");
await createProximityPresentationSession({
deviceEngagement: qrData,
onEstablished: async () => {
console.log("Session established successfully");
setLoadingMessage("Requesting verification data...");
try {
const response = await sendProximityPresentationRequest({
mobileCredentialRequests: [
{
docType: "org.iso.18013.5.1.mDL",
namespaces: {
"org.iso.18013.5.1": {
family_name: false,
given_name: false,
birth_date: false,
},
},
},
],
});
if (response.isErr()) {
throw new Error(`Failed to verify presentation: ${response.error.message}`);
}
setLoadingMessage("Verifying credentials...");
setVerificationResults(response.value);
setShowVerificationResults(true);
await terminateProximityPresentationSession();
} catch (error) {
console.error("Error during presentation request:", error);
Alert.alert("Error", "Failed to verify mDocs");
await terminateProximityPresentationSession();
} finally {
setLoadingMessage(false);
}
},
onTerminated: () => {
console.log("Session terminated");
setLoadingMessage(false);
},
onError: (error) => {
console.error("Session error:", JSON.stringify(error, null, 2));
Alert.alert(
"Error",
`Session failed: ${error.message || JSON.stringify(error)}`,
);
setLoadingMessage(false);
},
});
console.log(
"createProximityPresentationSession call completed (waiting for callbacks)",
);
} catch (error) {
console.error("Error during QR code processing:", error);
Alert.alert(
"Error",
`Failed to process QR code: ${error instanceof Error ? error.message : String(error)}`,
);
setLoadingMessage(false);
}
};
```
This function requests an mDL (mobile driver's license) credential with specific data elements: family\_name, given\_name, and birth\_date.
3. Uncomment the QRScannerModal import at the top of the file to integrate the QR Scanner modal:
```tsx title="App.tsx"
import { QRScannerModal } from "./QRScannerModal";
```
4. Paste the following code under the `// Verify mDocs - Step 1.4: Use QRScannerModal` comment to render the QR Scanner modal component in your app:
```tsx title="App.tsx"
setIsScanning(false)}
permission={permission}
requestPermission={requestPermission}
onQRCodeDetected={handleQRCodeDetected}
/>
```
5. Add the following code under the `Verify mDocs - Step 1.5: Create Scan QR Code Button` comment to create a button that opens the QR Scanner modal:
```tsx title="App.tsx"
setIsScanning(true)}
>
Scan QR Code
```
**Step 2: Display verification results**
1. Create the Verification Results modal in a new file called `VerificationResultsModal.tsx` and pasted the following code into it. `MobileCredentialResponse` is the type that holds the verification results from the MATTR Verifier SDK. The modal will display the verification results, including any claims and errors.
```tsx title="VerificationResultsModal.tsx"
import type { MobileCredentialResponse } from "@mattrglobal/mobile-credential-verifier-react-native";
import { Modal, SafeAreaView, ScrollView, Text, TouchableOpacity, View } from "react-native";
import { styles } from "./styles";
interface VerificationResultsModalProps {
visible: boolean;
onClose: () => void;
verificationResults: MobileCredentialResponse | null;
}
export function VerificationResultsModal({ visible, onClose, verificationResults }: VerificationResultsModalProps) {
if (!visible || !verificationResults) return null;
// Helper function to render different claim value types
function renderClaimValue(claim: any): string {
if (!claim) return "undefined";
if (claim.type === "array" || claim.type === "object") {
return JSON.stringify(claim.value);
}
return String(claim.value);
}
return (
Verification ResultsClose
{verificationResults.credentials && verificationResults.credentials.length > 0 ? (
{/* Basic verification status */}
{verificationResults.credentials[0].verificationResult?.verified
? "✓ Verified"
: "✗ Verification Failed"}
{verificationResults.credentials[0].docType}
{/* Display the raw credential data */}
{verificationResults.credentials.map((credential, credIndex) => (
{/* Claims data */}
{credential.claims &&
Object.keys(credential.claims).map((namespace, nsIndex) => (
{namespace}
{credential.claims &&
Object.entries(credential.claims[namespace]).map(([key, value], idx) => (
{key}:{renderClaimValue(value)}
))}
))}
{/* Error information */}
{!credential.verificationResult?.verified && credential.verificationResult?.reason && (
Verification Failed:Type: {credential.verificationResult.reason.type}Message: {credential.verificationResult.reason.message}
)}
{/* Claim errors if any */}
{credential.claimErrors && Object.keys(credential.claimErrors).length > 0 && (
Claim Errors
{Object.entries(credential.claimErrors).map(([namespace, errors]) =>
Object.entries(errors).map(([elementId, errorCode]) => (
{namespace}.{elementId}:
Error: {errorCode}
))
)}
)}
))}
) : (
No data available
)}
);
}
```
2. Return to your `App.tsx` file and uncomment the VerificationResultsModal import to integrate the verification results modal:
```tsx title="App.tsx"
import { VerificationResultsModal } from "./VerificationResultsModal";
```
3. Add the following code under the `// Verify mDocs - Step 2.3: Use VerificationResultModal` comment to display the verification results modal when verification is complete:
```tsx title="App.tsx"
setShowVerificationResults(false)}
verificationResults={verificationResults}
/>
```
## Test the end-to-end workflow [#test-the-end-to-end-workflow]
1. Run the verifier app. The MATTR Labs test issuer certificate is registered with the SDK
automatically on first launch.
2. Open your **holder testing device** and launch the **GO Hold example app**.
3. Select the **Wallet** button.
4. Locate the mDoc claimed as part of the [prerequisites](#mdoc) for this tutorial and select the
**share button** to display a QR code.
5. Use your **verifier testing device** and select the **Scan QR Code** button.
6. Use the **verifier testing device** to scan the QR code displayed on the **holder testing
device**.
7. Use the **holder testing device** to consent to sharing the information with the verifier.
8. Use the **verifier testing device** and select the **View response** button.
You should see a result similar to the following:
1. The wallet app user creates a QR code to initiate the proximity presentation workflow.
2. The verifier app scans the QR code, establishes a secure connection and sends a presentation
request.
3. The wallet app user reviews the presentation request and agrees to share matching mDocs with the
verifier.
4. The verifier app receives and verifies the mDocs included in the presentation response.
5. The verifier app user views the verification results.
Congratulations! Your verifier application can now verify mDocs presented via a proximity
presentation workflow, as per [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
## Summary [#summary]
You have just used the [mDocs Verifier SDKs](/docs/verification/remote-mobile-verifiers/sdks/overview) to build
an application that can verify an [mDoc](/docs/concepts/mdocs) presented via a
[proximity workflow](/docs/verification/in-person-overview) as per
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html):
This was achieved by building the following capabilities into the application:
* Initialize the SDK, so that your application can use its functions and classes.
* Register a trusted issuer certificate, which enables your application to verify mDocs issued by
that issuer.
* Scan a QR code presented by a wallet application and establish a secure communication channel.
* Send presentation requests to the wallet application, receive a presentation response and verify
its content.
* Display the results to the verifier app user.
## What's next? [#whats-next]
* You can check out SDKs reference documentation to learn more about available functions and
classes:
* [iOS](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk)
* [Android](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/)
* [React Native](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest)
* You can implement
[NFC based device engagement](/docs/verification/in-person-quickstart#listen-to-nfc-session-requests)
capabilities (currently supported by the Android Verifier SDK and the React Native SDK for Android platforms only).
# Credential Verification
URL: /docs/verification
[Mobile documents (mDocs)](/docs/concepts/mdocs) are the emerging standard for high-assurance digital
credentials. [Mobile driver's licenses (mDLs)](/docs/concepts/mdl) are the most prominent and most
adopted example to date, but the same verification capabilities apply to any high-assurance
credential built on the mDoc family of standards, including national identity cards, residence
permits, professional licenses, and other [credentials beyond the driver's
license](/docs/concepts/mdocs-beyond-mdl).
As an implementer, you need a verification solution that handles the complexity of standards
compliance, trust establishment, and cross-platform support, without requiring deep cryptographic
expertise. This overview walks you through the key decisions and steps for adding mDoc verification to
your application, whether you're verifying in person at a counter, remotely through a website, or
within a native mobile app. Start here, then follow the pages in order or jump to the topic you need.
## How mDoc verification works [#how-mdoc-verification-works]
An mDoc is a digital credential stored in a holder's wallet, issued by a trusted authority. Unlike
traditional document scans or OCR (optical character recognition)-based checks, mDoc verification
is cryptographic: your application validates a digitally signed credential against a trusted
issuer's certificate chain.
A complete verification covers both identity and information assurance:
* **Identity assurance**: resolve the issuer's identifier and confirm the credential was signed by
a trusted issuer, typically by checking the issuer's certificate against a local trust list or an
[external trust registry](/docs/digital-trust-service).
* **Information assurance**: verify the digital signature to confirm integrity, validate the
credential format against its referenced specification, and check that the credential is currently
active (not expired or [revoked](/docs/issuance/revocation/overview)).
Verifying a credential does not include evaluating the truth of the claims encoded in the
credential. Verification confirms only that these are the same claims signed by the issuer and
that the credential has not been tampered with.
In practice, this means:
* **No visual inspection required**: Verification is automated and tamper-evident.
* **Selective disclosure**: Holders share only the data points you request (e.g., "over 18" without
revealing full date of birth). See [selective disclosure](/docs/concepts/selective-disclosure) for
the underlying mechanism.
* **Offline capable**: In-person verification can work without network connectivity.
* **Standards-based**: Built on [ISO/IEC 18013-5, 18013-7 and
23220](/docs/concepts/iso-mdoc-standards), ensuring interoperability across wallets and issuers.
## Underlying platforms [#underlying-platforms]
You can use different MATTR VII, MATTR Pi and/or MATTR GO capabilities to verify different
[credential formats](/docs/concepts/formats-overview) based on your use case:
* **MATTR VII**:
* [In-person](/docs/verification/in-person-overview) verification of
[CWT and Semantic CWT credentials](/docs/concepts/cwt).
* [Remote](/docs/verification/remote-overview) verification of
[mDocs](/docs/verification/remote-overview#mdocs).
* **MATTR Pi**:
* [In-person](/docs/verification/in-person-overview) verification of
[CWT and Semantic CWT credentials](/docs/concepts/cwt).
* [In-person](/docs/verification/in-person-overview) and
[Remote](/docs/verification/remote-overview) verification of [mDocs](/docs/concepts/mdocs).
* **MATTR GO**:
* [In-person](/docs/verification/in-person-overview) verification of
[CWT and Semantic CWT credentials](/docs/concepts/cwt) and [mDocs](/docs/concepts/mdocs).
## Explore the overview [#explore-the-overview]
Work through these pages to design your verification solution from simple to more complex concepts:
1. [Choose your verification channel](/docs/verification/choosing-a-channel): in-person, remote web, or remote mobile.
2. [Decide what data you need to verify](/docs/verification/deciding-what-to-verify): selective disclosure and presentation requests.
3. [Embed the SDKs into your application](/docs/verification/embedding-the-sdks): integration approach, SDK options, and backend configuration.
4. [Establish trust with issuers](/docs/verification/establishing-trust): trusted issuer certificates and managed trust lists.
5. [Handling verification results](/docs/verification/handling-results): interpreting and acting on results.
6. [Frequently asked questions](/docs/verification/faq): answers to common verification questions.
# OpenID for Verifiable Presentations (OID4VP)
URL: /docs/verification/oid4vp
MATTR's remote mDocs verification capabilities implement the
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html) specification. While
OID4VP supports online presentation and verification of other credential formats, MATTR's
implementation is specific to online presentation and verification of mDocs, as per
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) Annex B and the draft version of
ISO/IEC 18013-7 Annex D.
## Basic OID4VP workflow [#basic-oid4vp-workflow]
The interaction comprises the following steps, based on the
[OAuth 2.0 mechanism](https://www.rfc-editor.org/info/rfc6749):
### Authorization request [#authorization-request]
When a user interacts with a verifier application which requires presenting an mDoc, the verifier
application responds with a request URI.
The verifier can configure the request URI to invoke any registered wallet application or define a
specific registered wallet application that will be used to handle this authorization request.
The wallet uses the request URI to retrieve the request object, which includes a presentation
definition detailing the information the verifier requires. It can include the type and format(s) of
credential(s) and any individual claims required from those credential(s).
### Gathering matching credentials [#gathering-matching-credentials]
The wallet determines what available mDocs match the presentation request, authenticates the holder
and requests for consent to present the required information. Upon consent, the wallet creates a
verifiable presentation(s) from matching verifiable credential(s) the holder has consented to share.
### Authorization response [#authorization-response]
The wallet sends the encrypted verifiable presentation response to the verifier’s application
response endpoint as an authorization response. This response includes a verifiable presentation
contained inside a `vp_token` parameter. The verifier provides a secure redirect URL in the response
to redirect the user back to where they started the interaction.
### Verifying the verifiable presentation [#verifying-the-verifiable-presentation]
Once the verifier receives the verifiable presentation, they can apply their own business logic and
validation rules to verify the content of the presentation.
OID4VP supports both [same-device](#same-device-verification-workflow) and
[cross-device](#cross-device-verification-workflow) verification workflows:
* **Same-device workflows** involve a single device which houses both the verifier and wallet
applications.
* **Cross-device workflows** involve two different devices:
* The first device houses the verifier application and issues a presentation request.
* The second device houses a wallet application that responds with the requested information.
## Same-device verification workflow [#same-device-verification-workflow]
Same-device workflows include a single device and two different applications:
* One web/mobile application is used to verify mDocs. It is used to create a presentation request
and verify the incoming presentation response. In our example this is the *BankApp*.
* One mobile application is used to hold and present mDocs. It is used to receive a presentation
request, authenticate the holder, gather matching credentials and share them (upon holder's
consent) with the verifier. In our example this is the *WalletApp*.
This workflow uses simple redirects to exchange the authorization request and response between the
two applications.
For example, consider a scenario where a user performs a financial transaction in a banking web
application using their mobile device. Upon initiating the transaction, the banking application
requests a proof of the user's identity. The digital wallet receives this request and seeks the
holder's consent to send a presentation response with a matching credential, such as a drivers'
license, national ID card or passport.
Upon receiving consent, the presentation response is signed using a private key with its
corresponding public key attested by the issuer and included in the credential. This private key is
typically stored in the mobile platform secure key store, which must be unlocked by biometrics,
verifying the identity of the holder.
The wallet then sends the presentation response to the verifier, who verifies both the credential
and the presentation's signature. If the public key found in the credential can be used to verify
the presentation’s signature, the verifier can confirm the identity of the entity presenting the
credential.
With authentication confirmed, the transaction proceeds, all within the confines of the user's
single mobile device.
Same-device flows typically have few variations and predominantly make use of mobile platform
features such as deep links and redirects to enable a verifying application to interface with a
wallet application.
The technical protocols underlying this flow allow both the verifier and wallet applications to be
either platform-native or web-based applications.
## Cross-device verification workflow [#cross-device-verification-workflow]
Cross-device verification workflows include two devices:
* One device is used to create the presentation request and verify the incoming presentation
response.
* One device is used to receive the presentation request, authenticate the holder, gather matching
mDocs and share them (upon holder's consent) with the verifier.
While the same-device flow uses a deep-link to invoke the digital wallet, in a cross-device flow the
link is rendered as a QR code, which is then scanned by the holder to invoke their digital wallet
and enable them to present the required credentials. Additionally, at the end of a cross-device flow
the holder is not redirected on their mobile device, but rather continues the interaction on the
device where they started it.
Following up on our previous example, consider a financial transaction conducted on a bank's online
portal, where the user begins the interaction on their desktop browser. When they are asked to
present an mDoc for verification, the user is presented with a QR code which they scan with their
mobile device. Upon scanning the QR code, their digital wallet is invoked and displays what
information is required by the bank for verification. The user consents to sharing the information,
and upon successful verification returns to their desktop browser to complete the interaction.
# Privacy in credential verification
URL: /docs/verification/privacy
Description: How MATTR's verification capabilities handle presented data, why verification does not require contacting the issuer, and what verifiers should do to minimize their data footprint.
Verification is the moment when a presented credential is checked against a set of trust rules.
This page describes how MATTR's verification capabilities handle the presented data, why
verification does not require contacting the issuer, and the practical guidance for verifiers who
want to minimize the data they hold.
For the broader picture, see [Privacy in MATTR's architecture](/docs/concepts/privacy).
## Verification is a local cryptographic check [#verification-is-a-local-cryptographic-check]
MATTR's verification capabilities check four things on each presented credential:
* The credential's digital signature is valid.
* The signing key chains back to a trusted issuer (either via a local trust list or via an
external [Digital Trust Service](/docs/digital-trust-service)).
* The credential structure conforms to the relevant standard (for example,
[ISO/IEC 18013-5](https://www.iso.org/standard/69084.html) for mDocs).
* Optional time-based checks (validity period, expiry, revocation status) where applicable.
All four checks can be performed locally by the verifier. There is no protocol-level requirement
to contact the issuer at presentation time. This is a deliberate architectural property: it lets
verification work offline, removes a single point of failure, and prevents the issuer from
observing verification activity.
For details on the checks performed on each credential type, see
[Credential verification](/docs/verification) and the format-specific overviews.
## What MATTR's verifier sees and what it does not retain [#what-mattrs-verifier-sees-and-what-it-does-not-retain]
The verifier receives only the attributes the holder consented to release. With selective
disclosure, this is typically a small subset of the credential. The verification capability:
* Computes the cryptographic checks.
* Returns the verification result to the calling application.
* Does not persist the credential content.
If the verifier's business application wants to retain attributes from the presentation (for
example, the user's age band to demonstrate compliance with an age-restricted purchase law), that
retention is an application-level decision. It is not done by MATTR's verification capability by
default.
The cryptographic proof itself can be discarded once the verification result has been recorded.
The proof is bound to a specific presentation and is not designed to be re-presented later.
Verifying a credential does not include evaluating the truth of the claims encoded in the
credential. It confirms only that the claims are the ones the issuer signed and that the
credential is structurally valid. The verifier still decides what business action to take based
on the disclosed claims.
## Selective disclosure in verification [#selective-disclosure-in-verification]
The flip side of [selective disclosure on the holder side](/docs/holding/privacy) is that the
verifier requests only what it needs.
When a verifier sends a presentation request, it specifies the attributes it wants. The wallet
shows the user what is being requested, the user consents, and the wallet produces a proof that
reveals only those attributes. The verifier receives the requested attributes and a cryptographic
proof that they came from a credential signed by the issuer.
This shifts the verifier's data discipline upstream. Instead of receiving the full credential and
then storing only what is needed, the verifier requests only what is needed in the first place
and never sees the rest.
See [Selective disclosure](/docs/concepts/selective-disclosure) for a fuller treatment of the
concept.
## Checking revocation without exposing the holder [#checking-revocation-without-exposing-the-holder]
Revocation is the area where verification comes closest to a live issuer interaction, and care is
needed to preserve privacy properties.
MATTR supports revocation via status lists. A status list is a published list of indices and
binary revocation values that the issuer maintains and the verifier fetches when it needs to
check status. Two privacy properties matter here:
* **Status lists do not contain personal information.** The list is a sequence of binary values
indexed by position. There is no link from a status list entry to the holder's identity.
* **Fetching a status list does not identify the holder.** The verifier requests the list as a
whole (or a chunk of it). The issuer can see that some verifier fetched the list, but cannot
tell which specific credential or holder triggered the check.
Verifiers who care about minimizing issuer-side correlation can cache status lists locally and
refresh them on a schedule independent of individual verifications.
## Trust establishment [#trust-establishment]
The verifier needs a way to know which issuers it should trust. There are two patterns:
* **Local trust lists.** The verifier maintains its own list of trusted issuer IACAs or DIDs and
evaluates each presentation against that list.
* **External trust via a Digital Trust Service.** The verifier consumes trust information from a
DTS (for example, via a VICAL or an Ecosystem policy). The DTS is the network operator's tool
for managing trust across many participants. See
[Privacy in digital trust services](/docs/digital-trust-service/privacy) for how DTS
consumption preserves the architecture's privacy properties.
In neither case does the verifier need to contact the issuer at presentation time.
## Practical guidance for verifiers [#practical-guidance-for-verifiers]
* Request only the attributes you need to make the decision you are making. If you are checking
age eligibility, request the age-attestation attribute, not the date of birth.
* Treat the cryptographic proof as transient. Record the verification result, not the proof
itself, unless you have a specific evidentiary requirement.
* Cache status lists locally and refresh them on a schedule. Do not fetch the list once per
verification if you can avoid it.
* If you need to retain disclosed attributes for compliance reasons, scope the retention period
to the minimum required by law and document the basis. The architecture supports a minimal
retention model; the deployment has to honor it.
* Be transparent with the holder about why you are requesting each attribute. Trust frameworks
increasingly require verifiers to declare a purpose as part of the presentation request, and
many wallets surface this to the user.
* Choose the regional MATTR deployment that matches your data residency obligations. Verifier
configuration data stays within the chosen AWS region except for listed sub-processors.
## Frequently asked questions [#frequently-asked-questions]
### Does the verifier need to contact the issuer to verify a credential? [#does-the-verifier-need-to-contact-the-issuer-to-verify-a-credential]
No. The credentials MATTR supports carry a cryptographic signature that can be verified locally
against the issuer's published public key or the relevant trust anchor. The verifier does not
need a live connection to the issuer at presentation time.
### Does MATTR's verifier store the presented credential? [#does-mattrs-verifier-store-the-presented-credential]
Not by default. The verification capability performs the cryptographic checks required to confirm
the credential's authenticity and validity, returns the result, and does not retain the
credential content. If the verifier wants to retain specific attributes, that retention is a
deliberate, application-level decision.
### Is the issuer told when a credential is verified? [#is-the-issuer-told-when-a-credential-is-verified]
No. The verification check is local. The only case where a verifier may interact with an
issuer-controlled endpoint is when checking a status list for revocation, and even that
interaction does not identify the holder.
### How do I check whether a credential has been revoked without leaking the holder's identity? [#how-do-i-check-whether-a-credential-has-been-revoked-without-leaking-the-holders-identity]
Revocation is checked against published status lists. A status list contains an index and a binary
revocation value. The verifier fetches the status list (which may cover many credentials) and
reads the relevant index. The issuer cannot tell which specific holder's status was checked.
### Can a verifier request more attributes than it needs? [#can-a-verifier-request-more-attributes-than-it-needs]
Technically, yes. The architecture cannot prevent a verifier from asking for too much. The
protections against over-collection are the user's ability to decline, the wallet's consent UI,
the issuer's policy, and accreditation rules in the relevant trust framework.
## Related reading [#related-reading]
* [Privacy in MATTR's architecture](/docs/concepts/privacy)
* [Selective disclosure](/docs/concepts/selective-disclosure)
* [Decentralized trust model](/docs/concepts/decentralized-trust-model)
* [Remote verification overview](/docs/verification/remote-overview)
* [In-person verification overview](/docs/verification/in-person-overview)
* [Digital Trust Service](/docs/digital-trust-service)
# React Native Verifier SDK v9.0.0 Migration Guide
URL: /docs/verification/react-native-9.0.0-migration-guide
Description: A guide to help developers migrate from React Native Verifier SDK v8.x to v9.0.0, including breaking changes, new features, and best practices.
## Overview [#overview]
This guide provides a comprehensive overview of the changes introduced in React Native Verifier SDK v9.0.0, including breaking changes, new features, and migration steps.
## Key Features [#key-features]
* **App to app verification**: The React Native Verifier SDK can now be used to request credentials from another app on the same device using OID4VP. This allows you to build verification flows directly into your apps and have a holder app on the same device respond.
* **Verify with Apple Wallet (iOS Only)**: The SDK can now request and verify a credential directly from an Apple Wallet using the Verify with Wallet API. Only available for iOS 16 and above.
* **Improved reliability in contactless flows**: Enhanced BLE performance delivers more consistent proximity credential exchanges and faster engagements.
* **Status Lists Draft 14 Support**: The SDK now supports the Token Status List Draft 14 specification while maintaining existing support for Draft 3.
* **Stronger cryptography and standards alignment**: Updated COSE algorithms (as per RFC 9864) strengthen cryptographic compatibility and ensure continued compliance with evolving standards.
* **General stability and performance improvements**: Multiple refinements reduce integration friction, increase consistency across mobile environments, and improve overall user experience.
For a detailed list of changes included in this release, refer to the [SDK Changelog](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/index.html#md:change-log).
## Breaking Changes [#breaking-changes]
| # | Element | Change | Impact |
| - | ------------------------------------------------------ | ------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| 1 | `initialize()` | Now accepts options and returns a `Result` type. | All call sites must handle the result and possible errors. |
| 2 | `sendProximityPresentationRequest` | Option renamed from `skipStatusCheck` to `checkStatus` with inverted semantics. | All call sites using `skipStatusCheck` must be updated. |
| 3 | `ProximityPresentationSessionTerminationErrorType` | New `Exception` value added. | Exhaustive switches must handle new case. |
| 4 | NFC error listener in `registerForNfcDeviceEngagement` | Now routes parse failures to `onError` instead of rethrowing. | Update error handling logic accordingly. |
## New Additions [#new-additions]
### Functions [#functions]
| Function | Description | Platform |
| ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
| `fetchAppleWalletConfiguration(options)` | Fetches Apple Wallet configuration needed before initiating the Apple Wallet verification flow. Returns an `AppleWallet` object. | iOS only |
| `handleDeepLink(options)` | Handles a deep link URL to continue the online presentation flow. | iOS only |
| `requestMobileCredentials(options)` | Remote app-to-app credential verification via Digital Credential Manager / OID4VP. Returns `OnlinePresentationSessionResult`. | Android (DCM/OID4VP), iOS (OID4VP only) |
| `destroy()` | Destroys the verifier SDK instance. Returns an error if called while the SDK is initialized; deinitialize the SDK before calling. | All |
| `getCurrentLogFilePath()` | Returns path to the Verifier SDK log file. | All |
### Updated Function Signatures [#updated-function-signatures]
* `initialize(options?)` — new `InitializeOptions` parameter:
* `loggerConfiguration?: LoggerConfiguration` — `logLevel`, `callbackLogLevel`, optional `logDir`, optional `callback`
* `platformConfiguration?: PlatformConfiguration` — `tenantHost: string` (required for `requestMobileCredentials`)
### New initialize Options [#new-initialize-options]
* `loggerConfiguration?: LoggerConfiguration` — configure SDK logging: logLevel, callbackLogLevel, logDir, callback on log events.
* `platformConfiguration?: PlatformConfiguration` — set MATTR VII tenant host for remote credential requests.
### New Types & Enums [#new-types--enums]
* **Online presentation (remote/app-to-app):**
* `OnlinePresentationSessionResult` — `{ sessionId, challenge?, mobileCredentialResponse?, error? }`
* `OnlinePresentationResultError` — `{ type: OnlinePresentationResultErrorType, message }`
* `OnlinePresentationResultErrorType` — `SessionAborted | VerificationError | ResponseError | WalletUnavailable | Unknown`
* **Apple Wallet:**
* `AppleWallet` — object with `requestMobileCredentials(challenge): Promise>`
* `RequestMobileCredentialsWithAppleWalletError` / `RequestMobileCredentialsWithAppleWalletErrorType`
* **`requestMobileCredentials` options & errors:**
* `RequestMobileCredentialsOptions` — `{ request, applicationId, walletProviderId?, challenge }`
* `RequestMobileCredentialsErrorType`
* `RequestMobileCredentialsError`
* **`handleDeepLink`:**
* `HandleDeepLinkOptions` — `{ url: string }`
* **`fetchAppleWalletConfiguration`:**
* `FetchAppleWalletConfigurationOptions` — `{ request, applicationId, merchantId }`
* `FetchAppleWalletConfigurationError` / `FetchAppleWalletConfigurationErrorType`
* **`initialize`:**
* `InitializeOptions`, `LoggerConfiguration`, `PlatformConfiguration`, `LogLevel` (`Off | Error | Warn | Info | Debug | Verbose`)
* `InitializeErrorType` = `SdkInitialized | StorageInitializedInBackground`
### New Error Values [#new-error-values]
| Location | New values |
| -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MobileCredentialVerifierErrorType` | `Connectivity`, `StorageInitializedInBackground`, `SdkInitialized`, `PlatformNotSupported`, `PlatformConfigurationInvalid`, `SessionTimedOut`, `SessionAborted`, `DigitalCredentialManager`, `UserCanceled`, `FailedToRequestMobileCredentials`, `AppleWalletNotAvailable` |
| `ProximityPresentationSessionTerminationErrorType` | `Exception` |
## Minimum Requirements [#minimum-requirements]
**iOS**
* iOS 15+ for core SDK functionality.
**Android**
* Android 7 / Nougat / API 24.
* The underlying Android Verifier SDK is built using Kotlin 2.0. This adds some intrinsic dependencies into your build tools.
* Kotlin 2.0 is supported from AGP version [8.5](https://developer.android.com/build/kotlin-support).
* AGP 8.5 is supported from Gradle version [8.7](https://developer.android.com/build/releases/about-agp) and Android Studio Koala [2024.1.1](https://developer.android.com/studio/releases).
## Migration Steps [#migration-steps]
### Update `initialize()` usage [#update-initialize-usage]
The `initialize()` function now accepts an optional options object and returns a `Result`. Update your code to handle the result and possible errors:
```diff
- await initialize();
+ const result = await initialize(options);
+ if (result.isErr()) {
+ // Handle error: result.error
+ }
```
### Update `sendProximityPresentationRequest` calls [#update-sendproximitypresentationrequest-calls]
The `skipStatusCheck` parameter has been renamed to `checkStatus` with inverted semantics. When `checkStatus` is `true` (default), the SDK will verify credential status. When `false`, it will skip status checking. Update all calls accordingly:
```diff
- const response = await sendProximityPresentationRequest({ ..., skipStatusCheck: true });
+ const response = await sendProximityPresentationRequest({ ..., checkStatus: false });
```
| Old Parameter | New Parameter | Mapping |
| ----------------------------------- | ------------------------------ | ------------------ |
| `skipStatusCheck = false` (default) | `checkStatus = true` (default) | No change needed |
| `skipStatusCheck = true` | `checkStatus = false` | Invert the boolean |
### Handle new `ProximityPresentationSessionTerminationErrorType.Exception` case [#handle-new-proximitypresentationsessionterminationerrortypeexception-case]
If you switch over `ProximityPresentationSessionTerminationErrorType`, add handling for the new `Exception` value.
### Update NFC error handling [#update-nfc-error-handling]
The NFC error listener in `registerForNfcDeviceEngagement` now routes parse failures to `onError` instead of rethrowing. Update your error handling logic accordingly.
# Remote verification
URL: /docs/verification/remote-overview
Remote verification allows verifiers to check the validity of a credential without the need for a
physical presence. This is particularly useful for scenarios where the verifier and holder are not
in the same location, such as online transactions or remote identity verification processes.
Remote verification is available for the following credential formats:
* [mDocs](#mdocs)
## mDocs [#mdocs]
The [ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html)
specification establishes interoperable methods for remote presentation and verification of
mDocs such as mobile driver’s licenses (**mDLs**) and other digital credentials.
### Verification requests [#verification-requests]
The [ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) specification defines two key
aspects of remote verification requests:
1. **Wallet interaction**: Defines how the request and response are transferred between the verifier
and the user's wallet:
* HTTP Redirects: Standard HTTP redirects are used to pass information between the verifier and
the wallet, typically via a web browser.
* [Digital Credentials API (DC API)](/docs/verification/remote-web-verifiers/dc-api/overview): A browser-integrated API for digital credential
interactions. The DC API allows web apps to communicate securely and seamlessly with wallet
apps, improving user experience by eliminating multiple browser redirects.
2. **Request type**: Defines how the verifier requests the credential from the user's device and how
should the device respond:
* Device retrieval: Based on the [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html)
standard and defined in ISO/IEC 18013-7 Annex A. The verifier directly requests a credential
from the user's device, and the device retrieves and presents the necessary credential data in
response.
* [OID4VP (OpenID for Verifiable Presentations)](/docs/verification/oid4vp): Based
on the [OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html) protocol
and defined in ISO/IEC 18013-7 Annex B. The verifier sends an authentication request, and the
device responds with a verifiable presentation containing the requested data.
#### ISO/IEC 18013-7:2025 Annexes [#isoiec-18013-72025-annexes]
ISO/IEC 18013-7:2025 defines specific annexes for each combination of wallet interaction and request
type:
| Annex | Request type | Transfer method | Platform support |
| ----- | ---------------- | ----------------------- | ------------------------------------------------------- |
| A | Device Retrieval | HTTPs | General purpose, browser-based interactions |
| B | OID4VP | HTTPs Redirects | General purpose, browser-based interactions |
| C | Device Retrieval | Digital Credentials API | Supported on **iOS** devices and the **Safari** browser |
The next iteration of ISO/IEC 18013-7 is expected to define an additional Annex D:
| Annex | Request type | Transfer method | Platform support |
| ----- | ------------ | ----------------------- | ---------------------------------------------------------------- |
| D | OID4VP | Digital Credentials API | Supported on **Android** devices and **Chromium-based** browsers |
#### Apple Verify with Wallet [#apple-verify-with-wallet]
In addition to the protocols defined in the ISO/IEC 18013-7 specification, Apple has introduced the
proprietary
[Verify with Wallet API](https://developer.apple.com/wallet/get-started-with-verify-with-wallet/).
This API can be used to streamline the verification process on iOS same-device flows by allowing
direct communication between the verifier and the wallet apps.
### Verification flows [#verification-flows]
The [ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) specification defines different
flows for requesting and presenting mDocs based on the type of verifier application and the
responding device:
* **Verifier web applications:** Typically use HTTP redirects or the Digital Credentials API to
invoke the wallet from a desktop or mobile browser. Web applications support same-device and
cross-device flows:
* **Same-device flow:** You start the verification experience in a mobile browser and are
redirected to a wallet app (where your mDoc is stored) installed on the same mobile device to
respond to the request.
For example, an online banking portal accessed from your mobile browser prompts you to open
your wallet app on the same phone to present your mDoc for identity verification.
* **Cross-device flow:** You start the verification experience in a desktop browser and use your
mobile device (where your mDoc is stored in a wallet app) to respond to the verification
request.
For example, you visit a government tax website on your desktop computer, and scan a QR code
with your mobile wallet app to present your mDoc.
* **Verifier mobile applications:** Can use platform capabilities or app-to-app communication to
initiate and complete the verification. Similar to web applications, mobile applications support
both same-device and cross-device flows:
* **Same-device flow:** You start the experience on a mobile app, and are redirected to a wallet
app installed on the same device to present the credential.
For example, a tax filing mobile app redirects you to your wallet app on the same phone to
verify your identity with your mDoc before filing your return.
* **Cross-device flow:** You start the experience on a **mobile app**, but use a **different
mobile device** to present the credential. This may happen if your credential is only
available on another device you own.
For example, you use your tablet to access a government service app, but the app enables you
to scan a QR code using your phone where the required mDoc is available in a wallet app.
The exact protocols and flows used depend on the requesting application and the user's wallet.
Different wallets and browsers support different combinations of these request types and transfer
methods.
### Verification channels [#verification-channels]
#### Verifier mobile applications [#verifier-mobile-applications]
The following table details the different verification channels for mobile applications, including
supported flows, supported wallets, underlying protocols and MATTR support status:
| Flow | Wallet | Protocol | MATTR support |
| -------------------------------------------------- | --------------------------------------------------------------------------------- | --------------------------- | ------------- |
| Same-device | Apple | Apple's Verify with Wallet | GA |
| Same-device | Compliant 3rd party iOS apps | ISO 18013-7 Annex B | GA |
|
Same-device
Cross-device
|
Google
Samsung
Compliant 3rd party Android apps
| ISO 18013-7 Annex D (Draft) | Preview |
### Verifier web applications [#verifier-web-applications]
The following table details the different verification channels for web applications, including
supported flows, supported wallets, underlying protocols and MATTR support status:
| Flow | Wallet | Protocol | MATTR support |
| -------------------------------------------------- | --------------------------------------------------------------------------------- | --------------------------- | ------------- |
|
Same-device
Cross-device
|
Apple
Compliant 3rd party iOS apps
| ISO 18013-7 Annex C | Preview |
|
Same-device
Cross-device
|
Samsung
Compliant 3rd party apps
| ISO 18013-7 Annex B | GA |
|
Same-device
Cross-device
|
Google
Samsung
Compliant 3rd party Android apps
| ISO 18013-7 Annex D (Draft) | Preview |
### Verification checks [#verification-checks]
The following standard checks are performed on all mDocs verification requests:
* Issuer IACA is valid as per [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
* Credential was issued by a trusted issuer (by checking the issuer's IACA against a
[local](/docs/verification/remote-verification-api-reference/trusted-issuers#create-a-trusted-issuer) or
[external](/docs/digital-trust-service) list of trusted issuers).
* Digital signature is valid.
* Credential structure complies with
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
The following checks are optional and defined as part of the verification request:
* Current time is after the beginning of the credential validity period.
* Current time is not after the end of the credential validity period.
* Credential has not been revoked.
# Remote verification implementation guidelines
URL: /docs/verification/remote-verification-implementation-guidelines
## Overview [#overview]
MATTR provides verification capabilities through both the MATTR Pi Verifier Web SDK and Verifier Mobile SDK, enabling relying parties to seamlessly request and verify verifiable credentials within their own applications.
This short guide explains the recommended implementation pattern for integrating the Verifier SDK, including the typical flow and its benefits. It also highlights a practice to avoid, so you can design a solution that is both technically robust and delivers a smooth, trusted experience for users.
## Recommended implementation pattern [#recommended-implementation-pattern]
The recommended design principle is to integrate the Verifier Mobile/Web SDK directly into the relying party’s web or mobile applications. This ensures that verification requests are trusted by wallets, and the user experience remains consistent and secure.
### Typical flow [#typical-flow]
1. User interacts with your relying party web or mobile application.
2. Application requests a credential presentation via the Verifier SDK.
3. Wallet validates the origin of the request against its trusted relying party list (e.g., web domain or mobile package ID).
4. Credential is presented back to your application.
5. Verifier SDK verifies the credential.
6. Interaction continues seamlessly.
### What this achieves [#what-this-achieves]
* **Trust and compliance**: OEM wallets (Apple, Google, Samsung) enforce strict origin checks, ensuring only registered relying parties can request credentials from these wallet applications. Third-party wallets (such as government-issued wallets) are also likely to adopt similar controls for both security and commercial reasons.
* **Smooth user experience**: Users never leave your application, keeping the interaction intuitive and consistent.
* **Future-proof integration**: Embedding the SDK avoids wallet validation failures and ensures your solution remains interoperable as wallet ecosystems evolve.
## Best practices [#best-practices]
### Avoid mediated verification windows [#avoid-mediated-verification-windows]
An alternative pattern sometimes considered is to redirect the user to a mediated or intermediate window where the SDK is embedded, perform the verification there, and then redirect the user back to the relying party application.
This approach should be avoided for two reasons:
1. Technical incompatibility: OEM and third-party wallets will reject credential requests from applications that are not directly registered as relying parties, making mediated flows unreliable.
2. Poor user experience: Redirecting users between apps or browsers fragments the journey, creates confusion, and increases the risk of dropouts.
Mediated flows can lead to failed integrations, blocked wallet access, and lost opportunities for credential-based interactions.
# Configuration
URL: /docs/digital-trust-service/rical-api-reference/configuration
## Update a RICAL configuration [#update-a-rical-configuration]
## Retrieve a RICAL configuration [#retrieve-a-rical-configuration]
## Delete RICAL configuration [#delete-rical-configuration]
# DTS root and intermediate CA certificates
URL: /docs/digital-trust-service/rical-api-reference/dts-root-ca-certificates
## Create DTS root CA certificate [#create-dts-root-ca-certificate]
## Retrieve all DTS root CA certificates [#retrieve-all-dts-root-ca-certificates]
## Retrieve all DTS root CA Certificates (public) [#retrieve-all-dts-root-ca-certificates-public]
## Retrieve a DTS root CA certificate [#retrieve-a-dts-root-ca-certificate]
## Update a DTS root CA certificate [#update-a-dts-root-ca-certificate]
## Delete a DTS root CA certificate [#delete-a-dts-root-ca-certificate]
## Retrieve a DTS root CA certificate Certificate Revocation List (CRL) (public) [#retrieve-a-dts-root-ca-certificate-certificate-revocation-list-crl-public]
## Create a DTS intermediate CA certificate [#create-a-dts-intermediate-ca-certificate]
## Retrieve all DTS intermediate CA certificates under a root CA [#retrieve-all-dts-intermediate-ca-certificates-under-a-root-ca]
## Retrieve a DTS intermediate CA certificate [#retrieve-a-dts-intermediate-ca-certificate]
## Update a DTS intermediate CA certificate [#update-a-dts-intermediate-ca-certificate]
## Delete a DTS intermediate CA certificate [#delete-a-dts-intermediate-ca-certificate]
# General
URL: /docs/digital-trust-service/rical-api-reference/general
## Create a RICAL [#create-a-rical]
## Retrieve all RICALs [#retrieve-all-ricals]
## Retrieve a RICAL [#retrieve-a-rical]
## Retrieve latest RICAL [#retrieve-latest-rical]
# Signers
URL: /docs/digital-trust-service/rical-api-reference/signers
## Create a RICAL signer [#create-a-rical-signer]
## Retrieve all RICAL signers [#retrieve-all-rical-signers]
## Retrieve a RICAL signer [#retrieve-a-rical-signer]
## Update a RICAL signer [#update-a-rical-signer]
## Delete a RICAL signer [#delete-a-rical-signer]
# Configuration
URL: /docs/digital-trust-service/vical-api-reference/configuration
## Update a VICAL configuration [#update-a-vical-configuration]
## Retrieve VICAL configurations [#retrieve-vical-configurations]
## Delete VICAL configuration [#delete-vical-configuration]
# DTS root and intermediate CA certificates
URL: /docs/digital-trust-service/vical-api-reference/dts-root-ca-certificates
## Create DTS root CA certificate [#create-dts-root-ca-certificate]
## Retrieve all DTS root CA certificates [#retrieve-all-dts-root-ca-certificates]
## Retrieve all DTS root CA Certificates (public) [#retrieve-all-dts-root-ca-certificates-public]
## Retrieve a DTS root CA certificate [#retrieve-a-dts-root-ca-certificate]
## Update a DTS root CA certificate [#update-a-dts-root-ca-certificate]
## Delete a DTS root CA certificate [#delete-a-dts-root-ca-certificate]
## Retrieve a DTS root CA certificate Certificate Revocation List (CRL) (public) [#retrieve-a-dts-root-ca-certificate-certificate-revocation-list-crl-public]
## Create a DTS intermediate CA certificate [#create-a-dts-intermediate-ca-certificate]
## Retrieve all DTS intermediate CA certificates under a root CA [#retrieve-all-dts-intermediate-ca-certificates-under-a-root-ca]
## Retrieve a DTS intermediate CA certificate [#retrieve-a-dts-intermediate-ca-certificate]
## Update a DTS intermediate CA certificate [#update-a-dts-intermediate-ca-certificate]
## Delete a DTS intermediate CA certificate [#delete-a-dts-intermediate-ca-certificate]
# General
URL: /docs/digital-trust-service/vical-api-reference/general
## Create a VICAL [#create-a-vical]
## Retrieve all VICALs [#retrieve-all-vicals]
## Retrieve a VICAL [#retrieve-a-vical]
## Retrieve latest VICAL [#retrieve-latest-vical]
# Signers
URL: /docs/digital-trust-service/vical-api-reference/signers
## Create a VICAL signer [#create-a-vical-signer]
## Retrieve all VICAL signers [#retrieve-all-vical-signers]
## Retrieve a VICAL signer [#retrieve-a-vical-signer]
## Update a VICAL signer [#update-a-vical-signer]
## Delete a VICAL signer [#delete-a-vical-signer]
# Core capabilities
URL: /docs/concepts/cwt/core-capabilities
## Compactness [#compactness]
The compact nature of the credential is an essential feature of offering credentials that work in
offline or paper-based contexts, as the credential payload is small enough to fit inside a QR code.
## Multi-Format [#multi-format]
CWT credentials can be converted to a number of formats. This includes PDF documents that can be
presented digitally or printed, as well as Apple and Google passes that can be stored in a digital
wallet. Thus, CWT credentials reduce the digital divide by supporting offline or paper-based
contexts alongside digital-first channels.
## Self contained [#self-contained]
CWT credentials are bearer credentials where all the assurance is fully self-contained within the QR
code, with no need for complex dynamic presentation capability from the holder.
## Offline verification [#offline-verification]
Holders present CWT credentials by showing a verifier a QR code representing their credential. This
can be scanned visually, or digitally transmitted by uploading an image or PDF. Verification can be
online via API or entirely offline. This makes CWT credentials ideal for rapid verification
scenarios and/or when there is no reliable internet connectivity.
## Revocation [#revocation]
You can use MATTR VII to control the status of issued CWT credentials. This allows verifiers to
obtain the revocation status of a CWT credential as it is being presented in a way that preserves
the privacy of the credential holder.
When a verifier requests a revocation list, the issuer cannot tell what credential are they checking
the status for. This means the issuer does not know how often or to whom a credential is being
presented, maintaining holder's privacy.
# Data structure
URL: /docs/concepts/cwt/data
## Overview [#overview]
CWT credentials are represented as QR codes, and as a result their payload size and complexity are
limited by the size of the generated QR code. To maximize content, CWT credentials are signed using
a [Concise Binary Object Representation (CBOR)](https://cbor.io/) data format called
[CBOR Web Token (CWT)](https://datatracker.ietf.org/doc/html/rfc8392). This data format is used to
represent credentials that become cryptographically verifiable when signed by an issuer.
CBOR is a binary data format derived from JSON and allows utilizing data types like numbers, strings
& arrays. Being binary, it offers a much more compact message size. Often when CBOR is being
discussed or documented, we can represent the same data using JSON to simplify data viewing and
modelling.
[JSON Object Signing & Encryption (JOSE)](https://datatracker.ietf.org/wg/jose/charter/) is used to
create ubiquitous JSON Web Tokens (JWT) in the signed (JWS) and encrypted (JWE) flavours. Similarly,
[CBOR Object Signing & Encryption (COSE)](https://datatracker.ietf.org/doc/html/rfc8152) is a
general-purpose digital signature encoding format that supports several different cryptographic
algorithms. It can be used to create CBOR Web Tokens (CWT) in corresponding CWS (signed) and CWE
(encrypted) formats.
CWT credentials issued by the MATTR platform are CWTs that have been cryptographically signed using
an issuer DID controlled by the tenant. This DID must be a [`did:web`](/docs/concepts/dids#didweb) using
a `P-256` key type. Our experience shows that DIDs of this type make offline verification easier by
using standard caching mechanisms and better support for the cryptographic primitives.
## Claims [#claims]
Information included in the credential is referred to as claims. CWT and Semantic CWT Credentials
can include two types of claims:
* **Standard claims**: These claims reference specific types of information such as the issuer of
the credential and its validity period.
* **Custom claims**: These can be any type of claims you wish to include in the credential. The
value of any custom claim must be either a boolean, a string or a number.
The following example is a signed CWT credential returned by the
[MATTR VII sign endpoint](/docs/issuance/direct-issuance-api-reference/cwt-issuance#sign-a-cwt-credential):
```json title="Signed CWT credential"
{
"id": "bKcrxojFSuSZvI5qhKInxA",
"decoded": {
"iss": "did:web:learn.vii.au01.mattr.global",
"nbf": 1704099600,
"type": "Course Credential",
"exp": 1767258000,
"name": "Emma Jane Tasma",
"code": "HS.278",
"certificationName": "Working at Heights",
"certificationLevel": "Level 4",
"issuerName": "Advanced Safety Training",
"status": {
"index": 3,
"url": "https://learn.vii.au01.mattr.global/v2/credentials/compact/revocation-lists/887cd140-e4d7-4518-b70f-305b23778848"
},
"jti": "bKcrxojFSuSZvI5qhKInxA"
},
"encoded": "CSC:/1/2KCE3IQEJB5DCMSMGZITM5QBE2QFSALWVQAXQI3ENFSDU53FMI5GYZLBOJXC45TJNEXGC5JQGEXG2YLUORZC4Z3MN5RGC3AFDJSZE7YQHIAACAACOFBW65LSONSSAQ3SMVSGK3TUNFQWYBA2NFLDPEDENZQW2ZLPIVWW2YJAJJQW4ZJAKRQXG3LBMRRW6ZDFMZEFGLRSG44HCY3FOJ2GSZTJMNQXI2LPNZHGC3LFOJLW64TLNFXGOIDBOQQEQZLJM5UHI43SMNSXE5DJMZUWGYLUNFXW4TDFOZSWYZ2MMV3GK3BAGRVGS43TOVSXETTBNVSXQGCBMR3GC3TDMVSCAU3BMZSXI6JAKRZGC2LONFXGOZTFPBYGS4TZNIZDAMRWFUYDCLJQGE5AAAIAACRAEAYDPB2WQ5DUOBZTULZPNRSWC4TOFZ3GS2JOMF2TAMJONVQXI5DSFZTWY33CMFWC6Y3POJSS65RSF5RXEZLEMVXHI2LBNRZS6Y3PNVYGCY3UF5ZGK5TPMNQXI2LPNYWWY2LTORZS6OBYG5RWIMJUGAWWKNDEG4WTINJRHAWWENZQMYWTGMBVMIZDGNZXHA4DIOAH3BAFA3FHFPDIRRKK4SM3ZDTKQSRCPRCYIA7RFUZYQI3RIGDHIGLAODJ6K2F245DTLIIKXAD35TORFQ7MVRJCIEPH6SC6NGA4HRMK76H5V6GXP66FFNX7MNYC6MYVU7ZLLXYVLXBU"
}
```
* `id` : Unique identifier of this CWT credential. This is the `jti` claim, which is the CBOR Web
Token (CWT) identifier detailed below.
* `decoded` : Includes both standard and custom claims. In the above example `iss`, `nbf`, `type`,
`exp`, `status` and `jti` are standard claims, while all other properties are custom claims
(`name`, `code`, `certificationName`, `certificationLevel` and `issuerName`).
* `encoded` : This is a [CBOR Web Token (CWT)](https://www.rfc-editor.org/rfc/rfc8392.html), which
is the decoded object represented as an encoded string. The `CSC:/1` prefix indicates the CWT
scheme (COSE Sign Compact profile, CSC) and the version (1).
The following claim types are predefined in the
[CWT standard](https://www.rfc-editor.org/rfc/rfc8392.html#page-5) to support interoperability.
You should only use predefined claims for their intended purpose. Attempting
to use any of the predefined claims names as the name of a custom claim that
represents a different data type will result in an error.
* `iss` (Issuer): Represents the party who issued the credential. This a required field for both
CWT and Semantic CWT credentials. Its decoded value must be a `did:web` using a `P-256` key type
that must be publicly available on the tenant.
* `sub` : (Subject): Represents the subject of the credential. When present, must be a text
string.
* `kid` (Key Identifier): References the signing key material. It must be present in the protected
header section. Its value is used with the `iss` value internally by MATTR VII to resolve the
referenced public key (`iss#kid`) and verify the signature of the COSE payload.
* `alg` (Signature Algorithm): Represents what algorithm is used for creating the signature. It
must be present in the protected header section and must be `ES256`. Its value is used
internally by MATTR VII to verify the signature of the COSE payload.
* `exp` (Expiration time): Represents the date and time after which the credential is considered
expired by the issuer. When present, must be a timestamp encoded as an integer in the
`NumericDate` format as specified in [RFC8392](https://tools.ietf.org/html/rfc8392) section 2.
* `nbf` (Not Before): Represents the earliest date and time at which the credential is considered
valid by the issuer. When present, must be a timestamp encoded as an integer in the
`NumericDate` format as specified in [RFC8392](https://tools.ietf.org/html/rfc8392) section 2.
* `iat` (Issued At): Represents the credential issuance time and date. When present, must be a
timestamp encoded as an integer in the `NumericDate` format as specified in
[RFC8392](https://tools.ietf.org/html/rfc8392) section 2.
* `cti` (CWT ID): Represents a unique credential identifier as a byte string. Since the decoded
object is represented as a JWT, the `cti` claim is mapped to the equivalent JWT `jti` claim,
presented as a string. The decoded `jti` value is encoded with `base64URL` without padding.
* `status` (Status): Provides information required to discover the current revocation status of
the credential.
* `type` (Type): Represents the unique type of this credential. When present, its value must be a
text string. MATTR highly encourages including a type claim for issued credentials to enable
various capabilities in consuming workflows, such as creating and validating
[Ecosystem policies](/docs/digital-trust-service/vical-guide#manually-publish-a-vical).
### Semantic CWT credentials claims [#semantic-cwt-credentials-claims]
Custom claims are embedded in Semantic CWT credentials into a standard `vc` property. This provides
semantic context to the claims and is also compliant with the
[W3C Verifiable Credentials Data Model 1.0](https://www.w3.org/TR/vc-data-model).
The following is an example of a signed Semantic CWT credential with custom claims inside a vc
object:
```json title="Signed Semantic CWT credential"
{
"id": "bKcrxojFSuSZvI5qhKInxA",
"decoded": {
"iss": "did:web:learn.vii.au01.mattr.global",
"nbf": 1516239022,
"type": "Course Credential",
"exp": 1516239922,
"jti": "urn:uuid:cc599d04-0d51-4f7e-8ef5-d7b5f8461c5f",
"vc": {
"@context": ["https://www.w3.org/2018/credentials/v1"],
"type": ["VerifiableCredential"],
"credentialSubject": {
"givenName": "Emma Jane",
"familyName": "Tasma",
"dob": "1979-04-14"
},
"status": {
"index": 3,
"url": "https://learn.vii.au01.mattr.global/v2/credentials/compact/revocation-lists/887cd140-e4d7-4518-b70f-305b23778848"
}
},
"encoded": "CSC:/1/2KCE3IQEJB5DCMSMGZITM5QBE2QFSALWVQAXQI3ENFSDU53FMI5GYZLBOJXC45TJNEXGC5JQGEXG2YLUORZC4Z3MN5RGC3AFDJSZE7YQHIAACAACOFBW65LSONSSAQ3SMVSGK3TUNFQWYBA2NFLDPEDENZQW2ZLPIVWW2YJAJJQW4ZJAKRQXG3LBMRRW6ZDFMZEFGLRSG44HCY3FOJ2GSZTJMNQXI2LPNZHGC3LFOJLW64TLNFXGOIDBOQQEQZLJM5UHI43SMNSXE5DJMZUWGYLUNFXW4TDFOZSWYZ2MMV3GK3BAGRVGS43TOVSXETTBNVSXQGCBMR3GC3TDMVSCAU3BMZSXI6JAKRZGC2LONFXGOZTFPBYGS4TZNIZDAMRWFUYDCLJQGE5AAAIAACRAEAYDPB2WQ5DUOBZTULZPNRSWC4TOFZ3GS2JOMF2TAMJONVQXI5DSFZTWY33CMFWC6Y3POJSS65RSF5RXEZLEMVXHI2LBNRZS6Y3PNVYGCY3UF5ZGK5TPMNQXI2LPNYWWY2LTORZS6OBYG5RWIMJUGAWWKNDEG4WTINJRHAWWENZQMYWTGMBVMIZDGNZXHA4DIOAH3BAFA3FHFPDIRRKK4SM3ZDTKQSRCPRCYIA7RFUZYQI3RIGDHIGLAODJ6K2F245DTLIIKXAD35TORFQ7MVRJCIEPH6SC6NGA4HRMK76H5V6GXP66FFNX7MNYC6MYVU7ZLLXYVLXBU"
}
}
```
* `jti` : Must be a valid URI according to the
[W3C Verifiable Credentials Data Model 1.0](https://w3c.github.io/vc-data-model). To meet these
requirements the decoded form of the `cti` claim must be a UUID.
# Implementation considerations
URL: /docs/concepts/cwt/implementation
We have developed the following customer and user experience implementation considerations through
our experience with how these work in real-world situations. Even though things may change from use
case to use case, these considerations offer flexibility.
The following sections provide guidance on how to mitigate common scenarios through your
solution/implementation:
* [Device Support](#device-support---will-my-end-users-device-be-supported): Will my end user’s
device be supported?
* [Accessibility](#accessibility---how-do-i-make-my-credential-accessible): How do I make my
credential accessible?
* [Scannability](#scannability---how-big-do-i-need-to-make-the-qr-code): How big do I need to make
the QR code?
* [Verification](#verification---what-instructions-can-i-provide-for-presenting-credentials-for-verification):
What instructions can I provide for presenting credentials for verification?
* [Internationalization](#internationalisation---how-can-i-support-or-display-multiple-languages):
How can I support or display multiple languages?
## Device Support – Will my end user’s device be supported? [#device-support---will-my-end-users-device-be-supported]
For CWT credential digital passes, we utilize Apple and Google’s built-in digital wallets – Apple
Wallet and Google Wallet (formally known as Google Pay). There are some ifs and buts – however, if
your user base is mostly on the latest OS’s supported list, you should be covered.
That being said, it is highly recommended to also implement CWT credential PDFs. Having both digital
passes and PDFs available provides your user choices – perhaps they don’t want to have a digital
pass, perhaps they don’t have a smartphone, perhaps they would prefer to have a backup hardcopy on
hand… the list goes on.
### For iOS users (Apple Wallet) [#for-ios-users-apple-wallet]
* iOS 10.0 or higher.
* Have Apple Wallet app installed.
### For Android users (Google Wallet) [#for-android-users-google-wallet]
* Android 5.0 (Lollipop) or higher.
* Play Protect certified.
* Have Google Wallet app installed and signed in with a Google account.
### Huawei/Honor users and non Play Protect certified devices [#huaweihonor-users-and-non-play-protect-certified-devices]
Some Huawei and Honor Android phones are not able to access Google services due to a trade ban -
this includes the Google Pay app. Phones that are launched after 15 May 2019 are affected by this
long-term issue across the industry.
If you have significant user base with these phones, potential workarounds include:
MATTR does not endorse any of the apps mentioned below or provide support for
the following methods. Please do your own testing to see if this is an
acceptable workaround for your users.
* Ask end-users to save the PDF on the phone or take a screenshot of the QR code – this allows for
offline usage and quicker access.
* Ask end-users to click “Add to Apple Wallet” and import the downloaded `.pkpass` file to a
supported wallet (such as [Wallet Passes](https://walletpasses.io/) or
[PassWallet](https://passwallet.net/)).
## Accessibility – How do I make my credential accessible? [#accessibility---how-do-i-make-my-credential-accessible]
### PDF [#pdf]
We’ve designed our PDF generator to use the same read-order and alt text as provided in your
template files. Alt text for dynamic text (text inside form fields) are defined in the
[config.json file](/docs/issuance/cwt-credential-templates/pdf-templates#configjson).
For in-depth details on how to create an accessible PDF see
[Adobe’s Guide](https://helpx.adobe.com/indesign/using/creating-accessible-pdfs.html).
### Apple digital pass [#apple-digital-pass]
Apple Pass does not currently support image alt text, so if you are adding text inside any images
make sure those aren’t the only instance it gets displayed. If the hero image includes the
credential name, perhaps also include it in the header and description field.
See [Design an Apple digital pass template](/docs/issuance/cwt-credential-templates/apple-templates) for field
customizations.
#### Voiceover read order [#voiceover-read-order]
1. `Advanced safety training` (pulled from `organizationName`)
2. `Certification, H S two hundred and seventy eight`
3. `Registered architects board`
4. `Name, tasma emma` (if `headerFields` are not present then the `primaryFields` are read instead)
5. `ticket fix`
6. `summer waves`
#### Voiceover read order [#voiceover-read-order-1]
1. `Certification, H S two hundred and seventy eight`
2. `Name, emma jane tasma`
3. `Certified for, level four`
4. `E X P, the first of january two thousand and twenty six, barcode, image`
### Google digital pass [#google-digital-pass]
Google Pass supports alt text for logo image, hero image, and QR code. Use the `description` key and
value to define alt text.
Read order is left to right and top to bottom.
See [Design a Google digital pass template](/docs/issuance/cwt-credential-templates/google-templates) for field
customizations.
## Scannability – How big do I need to make the QR code? [#scannability---how-big-do-i-need-to-make-the-qr-code]
### PDF [#pdf-1]
PDF QR code size can be defined as big or as small as you want. Depending on how big the credential
payload is, you might need to make the QR code bigger. We recommend starting at 33 mm (1.3 in) and
adjust bigger/smaller depending on space available and testing scannability with the encoded payload
(whether the camera app can scan the QR code).
We recommend an absolute minimum size of 30 mm (1.25 in).
Generated QR codes would have a \~1 mm (\~0.04 in) spacing around all 4 sides,
so adjust your `qrCode` form field with that in mind.
### Apple digital pass [#apple-digital-pass-1]
Apple Pass’s QR code is fixed and can’t be made bigger or smaller. It is worth noting that even
though it looks small in size, if the QR code is scannable at \~33 mm with a printed PDF, then end
users should not have any issues scanning an Apple digital pass. This is due to Apple devices having
a higher pixel density and being able to display it at a higher resolution.
### Google digital pass [#google-digital-pass-1]
Google Pass’s QR code is fixed and can’t be made bigger or smaller. However, Google designed the
Google Pay app to display the QR codes fairly big. Same as Apple digital passes, if the QR code is
scannable at \~33 mm with a printed PDF, then the end user should not have any issues scanning a
Google digital pass.
## Verification – What instructions can I provide for presenting credentials for verification? [#verification---what-instructions-can-i-provide-for-presenting-credentials-for-verification]
The [following
guide](https://www.figma.com/community/file/1120944380828784025) uses the
example *Working at Heights* certificate. Design source files are made
available for you to swap out images to your own credentials or to tweak the
text where needed.
### Digital PDF / Apple digital pass / Google digital pass [#digital-pdf--apple-digital-pass--google-digital-pass]
Upload the original PDF received from the
certificate issuer.
Don’t take a screenshot of the PDF zoomed out,
instead...
Enlarge the PDF so the QR code is in the centre of
the screen, then take the screenshot.
iOS: Screenshot from Apple Wallet
Android: Screenshot from Google Wallet
iOS / Android: You don’t need to show the “details”
page, that is for your own records only
### Printed copy [#printed-copy]
Scan your hard copy pass in color, at least 200dpi,
and as a PDF or jpg file. Did that not work? Try taking a photo instead...
Take a photo of the QR code, ensuring that it is: In
frame, Sharp and clear, Bright
Too far zoomed out. QR code is too small within the
picture.
Avoid gloss lamination as it causes glare. Instead,
use matte lamination.
Too dark and/or blurry. Try going to a brighter
space or turning on the flash.
Out of focus. Try moving your camera back a bit
until the QR code looks sharp.
## Internationalization – How can I support or display multiple languages? [#internationalization---how-can-i-support-or-display-multiple-languages]
We currently only offer support for Latin-based languages, however other scripts (such as Cyrillic,
Hebrew, Arabic) may still work.
### PDF [#pdf-2]
#### Using default fonts [#using-default-fonts]
As noted in the [Design a PDF template guide](/docs/issuance/cwt-credential-templates/pdf-templates), when no
custom font is specified the default Helvetica font is used. This font uses
[Windows-1252 encoding](https://en.wikipedia.org/wiki/Windows-1252) character sets which only
support 218 characters in the Latin alphabet.
**Full character set**
**Supported languages**
English, Irish, Italian, Norwegian, Portuguese, Spanish, Swedish, German, Finnish, Icelandic,
French, Faroese, Luxembourgish, Albanian, Estonian, Swahili, Tswana, Catalan, Basque, Occitan,
Romansh, Dutch (except the IJ/ij character), and Slovene (except the č character).
If your payload contains characters outside of this character set, you must use a custom font that
has character support for your languages.
#### Using custom fonts [#using-custom-fonts]
MATTR VII currently only allows for a 700kb maximum `template.zip` file for PDF templates. This
means CJK (Chinese, Japanese and Korean) fonts would generally be too big. If this affects your
implementation, please [contact us](http://mattr.global/contact).
#### Templates [#templates]
You may wish to make your PDF bilingual / trilingual / multilingual. Unlike apps or websites, PDFs
cannot display different languages depending on the user’s system language preferences. We recommend
two options to address this:
* **Display all the languages in a single template**: Real world example of this is a passport.
The labels have multiple languages on it, and often so does the disclaimer and informational
text. This approach may be useful if your use case requires cross-regional holder and verifiers,
and the verifier may not be familiar with the credential itself.
* **Use different template files for different languages**: If you are already obtaining the
user’s preferred language, you may wish to create multiple templates and switch between them as
needed. This is useful if the credential would generally be used within one region or presented
remotely.
### Apple digital pass [#apple-digital-pass-2]
Generally if the language you want to use is available as a system language on iOS, or if there are
no issues displaying characters from your language in iOS, then you should not run into any issues.
Apple Pass offers multi-language passes. This means a single `.pkpass` file can include multiple
languages. The language that gets displayed to the end user will default to the user’s device system
language. To learn more about this see
[Apple’s Developer Documentation](https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/PassKit_PG/Creating.html#//apple_ref/doc/uid/TP40012195-CH4-SW54).
### Google digital pass [#google-digital-pass-2]
Generally if the language you want to use is available as a system language on Android, or if there
are no issues displaying characters from your language in Android, then you should not run into any
issues.
Like Apple Pass, Google Pass offers multi-language passes. You would need to use the
`LocalizedString` API type. See
[Google Wallet API reference](https://developers.google.com/wallet/generic/rest/v1/LocalizedString)
for more information.
# CWT credentials
URL: /docs/concepts/cwt
CWT credentials are used to represent claims of data that can be cryptographically proven as
authentic while being compact enough to fit inside a QR code. They are ideal where high information
assurance is required but not high identity assurance about the entity presenting the credential.
The following sections will provide you with further information on CWT credentials:
* [Core capabilities](/docs/concepts/cwt/core-capabilities): Learn more about the core capabilities enabled when using
the CWT credential format.
* [Credential types](/docs/concepts/cwt/types): Learn about the two different types (CWT and Semantic CWT
credentials) and how to choose the one most suitable for your use case.
* [Data structure](/docs/concepts/cwt/data): Learn more about how CWT and Semantic CWT credentials are
built, and how to use standard and custom claims.
* [Implementation considerations](/docs/concepts/cwt/implementation): Learn about key considerations that
should be explored as part of implementing either a CWT or Semantic CWT credential into your
ecosystem.
* [CWT credentials across MATTR Platforms](/docs/concepts/cwt/mattr): Introduces key capabilities that are
available across MATTR platforms when you embed CWT credentials into your ecosystem.
CWT credentials can be converted to a number of formats. This includes PDF documents that can be
presented digitally or printed, as well as Apple and Google passes that can be stored in a digital
wallet. Thus, CWT credentials reduce the digital divide by supporting offline or paper-based
contexts alongside digital-first channels.
Holders present CWT credentials by showing a verifier a QR code representing their credential. This
can be scanned visually, or digitally transmitted by uploading an image or PDF. Verification can be
online via API or entirely offline. This makes CWT credentials ideal for rapid verification
scenarios and/or when there is no reliable internet connectivity.
CWT credentials are bearer credentials, where all the assurance is fully self-contained within the
QR code, with no need for complex dynamic presentation capability from the holder. However, this
means that the verifier cannot verify the identity of the person holding and presenting the
credential.
Discount vouchers, physical asset tags and luxury goods verification cards are all examples where
having authentic information through the supply chain increases the value of goods and services
produced, without relying on the identity of the holder.
Therefore, CWT credentials are ideal where high information assurance is required but not high
identity assurance about the entity presenting the credential.
## Frequently asked questions [#frequently-asked-questions]
### What is a CWT credential? [#what-is-a-cwt-credential]
A CWT credential is a verifiable digital credential built on the CBOR Web Token format. It
represents claims that can be cryptographically proven as authentic while remaining compact enough
to fit inside a QR code. CWT credentials are designed for use cases that require high information
assurance but not high identity assurance about the entity presenting the credential.
### When should I use a CWT credential instead of another credential format? [#when-should-i-use-a-cwt-credential-instead-of-another-credential-format]
Use a CWT credential when you need a compact, self-contained, QR-code-friendly credential that can
be verified offline and does not require the verifier to confirm the identity of the holder.
Typical examples include discount vouchers, physical asset tags, and luxury goods verification
cards, where the value lies in the authenticity of the information rather than the identity of the
bearer.
### How is a CWT credential presented and verified? [#how-is-a-cwt-credential-presented-and-verified]
Holders present CWT credentials by showing a verifier a QR code that represents the credential. The
QR code can be scanned visually or transmitted digitally by uploading an image or PDF. Verification
can be performed online via API or entirely offline using the credential's cryptographic signature.
This makes CWT credentials suitable for rapid verification scenarios and contexts without reliable
internet connectivity.
### Are CWT credentials bound to a specific holder? [#are-cwt-credentials-bound-to-a-specific-holder]
No. CWT credentials are bearer credentials. All the assurance is contained within the QR code
itself, so the verifier can rely on the authenticity of the information but cannot confirm the
identity of the person holding and presenting the credential. Where identity assurance is required,
an mDoc or another credential format with holder binding is more appropriate.
### Can CWT credentials be used without a smartphone? [#can-cwt-credentials-be-used-without-a-smartphone]
Yes. CWT credentials can be converted into a number of formats, including printable PDFs and Apple
or Google passes. This supports offline and paper-based contexts alongside digital-first channels,
which helps reduce digital exclusion where smartphone access cannot be assumed.
# CWT credentials across MATTR platforms
URL: /docs/concepts/cwt/mattr
When CWT credentials are added to a trust network, they can be applied to a wide variety of use
cases and business problems. CWT credentials are currently available across the following MATTR
platforms:
* **MATTR VII**: Offers a set of APIs that enable the following:
* Managing CWT credential configurations that serve as a template for signing and issuing CWT
credentials.
* Creating CWT credential offers.
* Signing CWT credentials using direct API requests, and then delivering to a holder’s digital
wallet via a separate endpoint.
* Formatting CWT credentials as QR codes, PDF documents, Apple or Google passes.
* Creating and managing templates for the different credential formats.
* Creating and managing DIDs which are required to issue CWT credentials.
* Verifying a signed CWT credential using a direct API request.
* **MATTR Pi**: Offers two SDKs to handle CWT credentials:
* MATTR Pi Holder SDK: Enables accepting a CWT credential offer and trigger its signing and
issuance to the wallet.
* MATTR Pi Verifier SDK: Enables verifying a signed CWT credential.
* **MATTR GO**:
* MATTR GO Wallet: Can be used to claim, hold and present CWT credentials.
* MATTR GO Verifier: Can be used to verify presented CWT credentials.
# Credential types
URL: /docs/concepts/cwt/types
MATTR VII supports two types of CWT credentials:
* **CWT credentials**: These are based on a concise, non-semantic model.
* **Semantic CWT credentials**: These are based on the
[W3C Verifiable Credential (VC) data model](https://www.w3.org/TR/vc-data-model/) and provide
more descriptive semantic meaning.
Semantic CWT credentials offer a standards-compliant way of constructing a verifiable credential
that can be understood by many parties in an interoperable way. However, the data model is
expressive in ways that will result in payloads significantly larger than the claims data. The
smaller the claims, the larger the ratio of the data model overhead. In general, the more compact a
credential is, the quicker and easier it is to manage by holders and verifiers.
The choice between the two comes down to how compact you need the credential to be versus how openly
you intend to share and exchange credentials across different domains and jurisdictions. Therefore,
choosing is a balance of standards compliance supporting an open ecosystem or a more compact
credential that can probably only be used within a closed ecosystem.
# Core Capabilities
URL: /docs/concepts/mdocs/core-capabilities
The core capabilities enabled by mDocs can be split into two main categories - enhanced security and
improved user experience.
## Security features [#security-features]
### Issuer data authentication [#issuer-data-authentication]
Relying parties need to be able to know what entity issued a presented credential, as that
information can affect their decision to trust information included in the credential. mDocs adhere
to a very specific and well-defined suite of data structures, procedures and cryptographic
algorithms defined in the ISO 18013-5 standard. This enables relying parties to verify the origin
and the issuer of a credential through a chain of linked certificates all the way to its root
Certificate Authority (CA).
Refer to the [Chain of trust](/docs/concepts/chain-of-trust) sub-section for more information.
### Device authentication [#device-authentication]
Relying parties must be confident that a presented credential was in fact issued to the device it is
presented from. To protect against malicious cloning, mDocs are bound to a mobile device and enable
verifying the binding between a credential and the mobile device used to present it. We refer to
this concept as device authentication. When a credential is issued to a mobile device, the latter
generates a private key that is locked to its tamper resistant key store. The issuer then includes
the corresponding public key in the credential to establish the device binding. That same private
key must be used whenever the credential is presented to a relying party.
### Holder authentication [#holder-authentication]
Relying parties must also be able to validate that the person presenting the credential is the same
person the credential was issued to. For this purpose, mDocs can include a portrait picture of their
holder, enabling the verifier to compare it with the person presenting them in person. This
comparison can be performed either manually or using facial recognition technologies.
While the ISO/IEC 18013-5 standard explicitly requires a portrait picture as part of a valid mDL
certificate, our current issuance and verification capabilities do not enforce it. That is because
mDocs were built to meet wider use cases, some of which may not require a portrait picture. Advanced
techniques enable confirming the authorized person is presenting the credential without sharing
their biometric data as part of the verification workflow.
### Session encryption [#session-encryption]
mDocs communication protocols establish an end-to-end encrypted channel to ensure the credentials
remain hidden and confidential from any possible eavesdroppers. This security feature applies to
both in-person and remote (online) verification workflows.
Refer to the [Verification](/docs/verification) section for more information.
## User experience [#user-experience]
### Multiple verification workflows [#multiple-verification-workflows]
mDocs offer tremendous value as a single credential can be used across different digital
interactions and workflows:
#### Remote interactions [#remote-interactions]
mDocs can be used in online interactions via either same-device or cross-device workflows as per
ISO/IEC 18013-7. In same-device workflow, the user completes the entire interaction on their mobile
device, whereas in cross-device workflows they begin an interaction on a different device and are
then redirected to the mobile device that holds the mDoc to complete the interaction.
#### In-person interactions [#in-person-interactions]
mDocs are constructed in a way that enables real time offline verification, with no reliance on
internet-based technologies. The two devices (holder’s and verifier’s) communicate via BLE and have
all the information required to complete a verification workflow using non-internet technologies and
communication protocols. This means that in-person verification workflows can be completed anywhere,
regardless of location and internet coverage.
Refer to the [Verification](/docs/verification) section for more information.
### Selective disclosure [#selective-disclosure]
mDocs allow relying parties to specifically request only the information they need from the holders
of these credentials. The holders, in turn, can consent to sharing selected information contained in
the credential while fully understanding what, with whom, and for what purpose they are sharing it.
This concept is known as *Selective Disclosure*.
Refer to the
[Structure to function](/docs/concepts/mdocs/structure-to-function#salt-values-support-unwanted-disclosures-protection)
sub-section for more information on how mDocs enable this capability.
## Revocation [#revocation]
mDocs issued using MATTR VII have the capability to be set with a status that can be checked by
Verifiers while preserving the privacy of the holder. This status can then be updated by Issuers to
indicate if the credential is permanently revoked, temporarily revoked, or still valid. The holder
can use their digital wallet to see the latest status of their mDocs at any time.
Refer to [Revocation](/docs/issuance/revocation/overview) to learn more about this capability.
# mDocs
URL: /docs/concepts/mdocs
mDocs are based on the [ISO/IEC 18013-5](https://www.iso.org/standard/69084.html) standard and
[18013-7](https://www.iso.org/standard/91154.html) technical specification. They are digital
identity documents designed to be stored on the holder’s mobile device, and can be verified either
in-person or remotely (online).
The key strength of mDocs over other digital credential technologies lies in their ability to
provide strong authentication and strong identification, supporting digital interactions that were
previously impossible due to high security risks. By offering increased levels of security for both
offline and online verification workflows, mDocs allow for seamless integration into a variety of
use cases across industries. They are particularly ideal for high assurance identity credentials
such as passports and national identification cards, as they offer added protection against forgery,
cloning, eavesdropping and impersonation.
Designed to be stored in a digital wallet on a mobile device, mDocs allow for a secure binding
between the mobile device and the credential, as well as a tighter native integration with iOS and
Android. This means credential verification workflows can leverage close proximity technologies such
as Bluetooth Low Energy (BLE).
The following sub-sections detail the standards and technologies which mDocs are based on, as well
as the key capabilities they offer across MATTR platforms:
* [Core Capabilities](/docs/concepts/mdocs/core-capabilities): Reviews the core capabilities available for mDocs, setting
the ground for the following subsections who explain how these capabilities are enabled.
* [Standards and Technologies](/docs/concepts/mdocs/standards-and-technologies): Describes the ISO/IEC standards mDocs are based
on, as well as some key concepts and technologies that would help you better understand mDocs.
* [From Structure to Function](/docs/concepts/mdocs/structure-to-function): Reviews the structure of mDocs
and mDocs presentations, and how that structure enables the key functionalities offered by mDocs.
* [mDocs across MATTR Platforms](/docs/concepts/mdocs/mattr): Introduces key capabilities that are available
across MATTR platforms when you embed mDocs into your network.
## Frequently asked questions [#frequently-asked-questions]
### What is an mDoc? [#what-is-an-mdoc]
An mDoc is a digital identity document designed to be stored on a holder's mobile device and
verified either in person or remotely. mDocs are based on the ISO/IEC 18013-5 standard and the
ISO/IEC 18013-7 technical specification. They are particularly suited to high assurance identity
credentials such as passports, national identification cards, and mobile driving licenses.
### What is the difference between an mDoc and an mDL? [#what-is-the-difference-between-an-mdoc-and-an-mdl]
An mDL (mobile driving license) is a specific type of mDoc that represents a driving license,
defined by ISO/IEC 18013-5. mDoc is the broader category of credential format defined by the same
standard, which can also be used for other high assurance identity documents such as passports or
national identification cards.
### How are mDocs verified? [#how-are-mdocs-verified]
mDocs are verified by checking the cryptographic signature applied by the issuer, the binding
between the credential and the holder's device, and the credential's status. Verification can be
performed in person using close proximity technologies such as Bluetooth Low Energy, or remotely
over the internet. Most verification steps can be performed offline because the signature does not
require a live lookup against the issuer.
### Are mDocs more secure than other digital credential formats? [#are-mdocs-more-secure-than-other-digital-credential-formats]
mDocs are designed for high assurance use cases. They support strong authentication and strong
identification through device binding, session encryption, and protections against forgery,
cloning, eavesdropping, and impersonation. Other formats may be more appropriate where these
protections are not required or where the credential is primarily about information assurance
rather than identity assurance.
### Do mDocs support selective disclosure? [#do-mdocs-support-selective-disclosure]
Yes. mDocs use a salted hashed claims structure that allows the holder to present only the
attributes a verifier needs, without revealing the rest of the credential. This means a verifier
can confirm a specific fact, such as proof of age, without learning unrelated personal data.
# mDocs across MATTR platforms
URL: /docs/concepts/mdocs/mattr
When mDocs are added to a trust network, they can be applied to a wide variety of use cases and
business problems. mDocs are currently available across the following MATTR platforms:
* **MATTR VII**: Offers a set of APIs that enable the following:
* Managing [mDoc credential configurations](/docs/issuance/credential-configuration/overview)
that serve as a template for signing and issuing mDocs.
* Creating [credential offers](/docs/issuance/credential-offer/overview) and issuing mDocs to
compliant digital wallets via an OID4VCI
[Authorization Code](/docs/issuance/authorization-code/overview) and/or
[Pre-authorized Code](/docs/issuance/pre-authorized-code/overview) flows.
* Manage the [revocation](/docs/issuance/revocation/overview) status of issued mDocs, including managing the
required Status List configurations, Status List signers and Status List signers certificates.
* Creating and managing the certificates required to sign mDocs. MATTR VII can be used to
manually create Issuing Authority Certificate Authority (IACAs) and is automatically creating
Document Signer Certificates (DSCs) when required. Alternatively, MATTR VII can be configured
to use [unmanaged certificates](/docs/concepts/chain-of-trust#external-certificates) that are managed and
uploaded by the issuer.
* **MATTR Pi**: Offers SDKs to handle mDocs holding and verification:
* [Holder SDKs](/docs/holding/sdk-overview): Enables claiming mDocs via an OID4VCI workflow and presenting it for verification
either in-person or remotely.
* [Verifier Mobile SDKs](/docs/verification/remote-mobile-verifiers/sdks/overview): Enables proximity and remote verification of a signed mDoc, as per
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
* [Verifier Web SDK](/docs/verification/remote-web-verifiers/sdks/overview): Enables remote verification of a signed mDoc, as per
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html).
* **MATTR GO**: Offers two white-labeled apps that can handle mDocs.
* [GO Hold](/docs/holding/go-hold/getting-started) can claim, hold and present mDocs
either in-person or remotely.
* [GO Verify](/docs/verification/go-verify/getting-started) can perform proximity
verification of a signed mDoc, as per
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
Currently our APIs only support the ECDSA with P-256 (ES256) algorithm for
issuer and device authentication. Contact us if you want to better understand
the rationale for this choice.
# Standards and technology
URL: /docs/concepts/mdocs/standards-and-technologies
This page details the following mDocs concepts:
* [Underlying standards](#underlying-standards): This will help you understand where mDocs are
positioned in relation to existing standards.
* [Foundational technologies](#foundational-technologies): This will help you understand what
technologies are being used to enable the core concepts of mDocs, and how they might interface
with your existing ecosystem.
## Underlying standards [#underlying-standards]
The [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html) standard was created to
standardize certain aspects in a Mobile Driver Licenses (mDLs) ecosystem. It defines the interface
for a mobile device to present an mDL to a verifier using a digital wallet in a close proximity
scenario. This includes data structure and important security features enabling the verifier to
validate the mDL.
The [ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) technical specification was
created to augment capabilities defined in the
[ISO/IEC 18013-5](https://www.iso.org/standard/69084.html) standard by enabling using a browser to
present an mDL to a verifier remotely (online). This standard specifies two protocols to support
online presentation of mDLs - RestAPI and
[OpenID for Verifiable Presentations (OID4VP)](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html).
MATTR mDocs implement the latter, which defines a mechanism on top of
[OAuth 2.0](https://www.rfc-editor.org/info/rfc6749) to enable online verification of verifiable
credentials, including mDLs.
The [ISO/IEC TS 23220 standard series](https://www.iso.org/standard/74910.html) (currently under
development) aims to generalize applications of this technology for more use cases, broadly referred
to as Mobile Documents or mDocs. ISO/IEC TS 23220-4 is compatible with ISO/IEC 18013-5 and ISO/IEC
18013-7 while introducing additional capabilities such as holder authentication.
MATTR mDocs have a generic format that can be presented with any claims and can be used in many
scenarios - purchasing age-restricted items, opening bank accounts, renting or sharing cars, going
through airport security, accessing secure locations and more.
## Foundational technologies [#foundational-technologies]
### Object representation [#object-representation]
mDocs verification workflows require compact data structures. Therefore, Concise Binary Object
Representation (CBOR) is the main format used to encode mDocs data structures.
CBOR is a binary data format designed to be interoperable with JSON while supporting more complex
data types. Being binary, it offers more compact messages, and allows encoding data directly without
converting it to a base64-encoded string first. In the context of mDocs, this is a big advantage
over JSON, which is a less space efficient data encoding method.
Refer to [CBOR.io](https://cbor.io/) to go deeper into CBOR.
### Object signing [#object-signing]
Personal information included in mDocs must be signed to be tamper proof. CBOR Object Signing and
Encryption (COSE) allows signing CBOR data structures in the same way JSON data structures can be
signed by JavaScript Object Signing and Encryption (JOSE).
mDocs use COSE to sign CBOR data structures, verifying the validity of the signed credential by
authenticating both the issuer and the device.
### Hashed salted claims [#hashed-salted-claims]
A hash is a cryptographic function that takes in data of any length, and creates a fixed length
unique output, referred to as a Hash (also known as a Digest). The algorithm type indicates the
fixed output length. For example, the SHA-256 algorithm generates 256 bits / 32 byte long hashes:
Valid hash functions must meet the following requirements:
* Collision resistant: Two distinct inputs must never result in the same hash.
* One-way: A Hash cannot be used to decipher the original input.
* Repeatable: The same data input should always generate the same output hash.
A salted hash value is the result of combining a unique random salt with a piece of data (e.g., a
claim) and applying a hash function, making it more secure by preventing attackers from easily
reversing or guessing the original data.
In the context of mDocs, salted hashed claim are used to enhance privacy by allowing holders to
reveal only specific information to relying parties, without disclosing unnecessary personal data.
This is referred to as selective disclosure. Each claim in the mDoc (e.g., date of birth or address)
is hashed with a unique salt and signed by the issuer, making it impossible to reverse-engineer the
original data unless the holder explicitly discloses it—even if the relying party receives a
credential that contains salted hashes of other undisclosed claims. This enables holders to reveal
only the necessary claim, such as their birthdate, without disclosing other claims like their
address.
For example, if an mDoc contains both a holder's birthdate and address, the credential will contain
salted hashes of each. When the holder needs to prove their age, they disclose only the birthdate.
The relying party receives the disclosed birthdate, along with its corresponding hash and salt from
the credential and can confirm the holder meets the age requirement without accessing other
information, such as the address, thereby preserving the holder’s privacy.
### X.509 certificates [#x509-certificates]
mDocs are high assurance identity credentials. Verifiers must be able to authenticate their issuers
to enable digital trust workflows within the ecosystem.
X.509 certificates are a standardized format for digital certificates, fundamental to Public Key
Infrastructure (PKI). Each certificate includes a public key and details about the certificate
itself, binding this information to a specific entity. Public key validation and verification
processes guarantee that the information in the certificate aligns with the claimed entity,
maintaining the overall security and trustworthiness of the PKI infrastructure.
X.509 certificates commonly use certificate chains which encompass multiple layers of digital
certificates to establish a chain of trust. The PKI hierarchy begins with a root certificate, acting
as the highest authority that issues intermediate certificates, creating a chain of trust leading to
end-entity certificates (such as mDocs). Certificate chain validation ensures the integrity of this
hierarchy, confirming that each certificate in the chain is legitimate, signed by its issuer and has
not been revoked.
In the context of mDocs, an
[Issuing Authority Certificate Authority (IACA)](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca)
and a [Document Signer Certificates (DSC)](/docs/issuance/certificates/overview#document-signer-certificate-dsc) are
both X.509 certificates.
# Structure to Function
URL: /docs/concepts/mdocs/structure-to-function
To properly use mDocs in a digital trust ecosystem, one must gain a good understanding of their
structure, and how it relates to their function.
## Data structure [#data-structure]
### From claims to an mDoc [#from-claims-to-an-mdoc]
mDocs issuers ascertain verifiable information about holders they issue credentials to. This
information is referred to as *claims* and is stored in systems which are referred to as *claims
sources*. When these claims are issued in an mDoc they are grouped into *namespaces* to prevent
collision between claim names.
When creating a presentation from an mDoc and sharing it with a verifier, it only includes a subset
of the raw claims from the mDoc. These are the elements required by the verifier that the holder had
consented to disclose. By comparing these elements to the corresponding salted hashes in the Mobile
Security Object (MSO), the verifier can ascertain the claims' integrity.
The following diagram depicts the mDoc and Presentation architecture which includes the
aforementioned elements:
* MSO: A COSE\_sign1 structure (CBOR standard for presenting digital signatures) comprising several
components:
* `Header` : Details the algorithm being used.
* `DSC` : The Document Signer Certificate (DSC) is included in the MSO and can be used
alongside the IACA certificate to verify the MSO signature.
* `Issuer Signature` : The signature of the mDoc’s issuer.
* Payload: The element signed by the DSC. It includes the following components:
* Device public key.
* Credential validity period.
* Metadata (key type, authorizations, etc.).
* Element Digest/Hash: An array that includes all the salted hashes of issuer signed
claims.
* `All Elements` : Includes the raw claims grouped by namespaces. Each element within a namespace
includes the claim name, claim value and salt value.
### From an mDoc to a presentation [#from-an-mdoc-to-a-presentation]
The following diagram depicts what elements are carried through when we creating a presentation from
an mDoc and sharing it with a verifier:
* `DSC` : Required to verify the MSO signature.
* `Selected Elements` : The subset of raw claims from the mDoc. These are the elements required by
the verifier and agreed to be shared by the holder. Comparing these elements to the
corresponding hashes in the MSO, the verifier can ascertain their integrity.
* `Device Auth` : A signature produced using the private key associated with the device public key
in the MSO over the unique session data.
## Function [#function]
The structure described above serves the following capabilities:
### Salted hashed claims enable selective disclosure [#salted-hashed-claims-enable-selective-disclosure]
The MSO is always included in an mDoc presentation to enable validating the issuer signature.
Selective disclosure would not be possible if all claims in the MSO were signed then revealed to
every verifier. The salted hashing of claims into the MSO ensures that it provides no data and only
proves that what is disclosed and presented by the holder was indeed signed by the issuer.
When a verifier is validating an mDoc presentation, they re-compute the hash/digest for each
revealed claim and its salt, and try to match it to the one referenced in the MSO. If the match is
successful then the integrity of the claim back to the issuer is proven. If they don’t match, the
value is rejected and the presentation is invalid. This structure enables the holder to selectively
disclose a subset of their credential claims, and the verifier to verify this subset of claims.
### Salt values support unwanted disclosures protection [#salt-values-support-unwanted-disclosures-protection]
Since hash functions are repeatable, brute forcing might enable verifiers to guess unrevealed claims
when their enumeration is very limited (for example nationality can only be a two letter country
code defined in [ISO 3166-1](https://www.iso.org/iso-3166-country-codes.html)).
To solve this, the issuer adds a unique random massive number (commonly referred to as *nonce* or
*salt*) to each claim value for every hash that is created. This means you can only reconstruct the
hashed claim if you have both the salt and the claim value:
### Device Auth provides anti-cloning and replay protection [#device-auth-provides-anti-cloning-and-replay-protection]
The `Device Auth` element authenticates that the device presenting the credential is the same one
that the credential was issued to.
When an issuer issues an mDoc, the device generates a private key that is locked to its hardware.
That same private key must be used whenever the credential is presented to a verifier, both
in-person and remotely (online). Assuming the private key is exclusively bound to a device, it
prevents anti-cloning, and the relying party can trust that the credential is both valid and is
presented by the intended device.
Device authentication can be implemented in one of two methods, both supported by the ISO/IEC
18013-5:2021 specification:
* **Signature authentication**: The holder uses the device key to produce a digital signature that
is included in the device’s response, authenticating the holder’s possession of the device key
to the verifier. This is considered a non-repudiable method.
* **ECDH-agreed Mac based authentication**: The holder and the verifier use their own private key
and the other parties public key to generate a mutually agreed key using ECDH (Elliptic Curve
Diffie Helman). This prevents scenarios where a verifier may attempt to share the response with
a 3rd party without the holder’s consent. Since both parties (the holder and the verifier) could
generate the same MAC, the verifier cannot prove to a 3rd party that it was the holder who
generated the MAC. This method is considered to be non-repudiable to the verifier, but it is
repudiable to a 3rd party as it can’t tell whether the MAC was produced by the holder or the
verifier.
Replay protection is achieved using the same mechanism by signing over the session transcript,
acting as a fingerprint of the current session between the verifier and the holder.
### Issuer Auth enables offline verification [#issuer-auth-enables-offline-verification]
The mDoc is constructed in a way that the verifier only needs the IACA’s certificate of the issuer
to verify a presented credential. Everything else (DSC, MSO) exists within the mDoc itself, enabling
offline verification as no internet access is required to retrieve any information.
### Status field enables revocation checking [#status-field-enables-revocation-checking]
[Revocable](/docs/issuance/revocation/overview#mdocs) mDocs include a status reference in their MSO payload. The standardized field name is `status`, though legacy credentials issued before adoption of Draft 14 of the Token Status List specification use `_status`. Both field names contain identical information and serve the same purpose. For more details, see [mDocs revocation](/docs/issuance/revocation/overview#mdocs).
## Signed mDoc payload [#signed-mdoc-payload]
Let's look into the structure of a signed mDoc issued by a MATTR VII tenant:
```json title="Signed mDoc example"
{
"id": "8669f836-b3b2-43f3-aecd-d92187cb1cac",
"encoded": "omppc3N1ZXJBdXRohEOhASahGCFZApcwggKTMIICOKADAgECAgpSc4u6ewLWeF9jMAoGCCqGSM49BAMCMC4xCzAJBgNVBAYTAkFVMR8wHQYDVQQDDBZsYWJzLm1hdHRybGFicy5jbyBJQUNBMB4XDTI0MDcyOTEwMTMwMVoXDTI1MTAyNzEwMTMwMVowOTELMAkGA1UEBhMCQVUxKjAoBgNVBAMMIWxhYnMubWF0dHJsYWJzLmNvIERvY3VtZW50IFNpZ25lcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIwfV3GMKmD2/AeX0nyM17WWpDcu0ow/l+LwX7JsrvMmGV2IJmkJnCLPlFJS8OEZCH2Awx6dcHZM9ypPWPxRFJ+jggExMIIBLTAdBgNVHQ4EFgQUeDSAUoc4IOG98TfH4oBwEAXzjCEwHwYDVR0jBBgwFoAUjpQIFBA5F7EMBwhiw7HRjumaE/4wDgYDVR0PAQH/BAQDAgeAMCQGA1UdEQQdMBuGGWh0dHBzOi8vbGFicy5tYXR0cmxhYnMuY28wJAYDVR0SBB0wG4YZaHR0cHM6Ly9sYWJzLm1hdHRybGFicy5jbzB4BgNVHR8EcTBvMG2ga6BphmdodHRwczovL2xhYnMudmlpLmF1MDEubWF0dHIuZ2xvYmFsL3YyL2NyZWRlbnRpYWxzL21vYmlsZS9pYWNhcy9mNDU5ZjliMi0xNzZkLTQ5NzAtYjRjYi0xZGFhOGVmOGE0ZTUvY3JsMBUGA1UdJQEB/wQLMAkGByiBjF0FAQIwCgYIKoZIzj0EAwIDSQAwRgIhAKKrbWcL0zSR7v+eMVnhvGOPfYN5Z1N7Z3bwRmfC9UTuAiEA3VS+f1ejLdHkyBKnzUjPBjOHYZ0uFX1vMS/QShgy0mNZ5MXYGFnkwKdndmVyc2lvbmMxLjBvZGlnZXN0QWxnb3JpdGhtZ1NIQS0yNTZsdmFsdWVEaWdlc3RzoXFvcmcuaXNvLjE4MDEzLjUuMbIAWCBHKXMYF97lb0Z04rfrV2P4jXk9xglx/6jKG6j2DciXxgFYIDs6fkgoiSVVbePNCUWpJI6L+rOFssU9ezcU4yNO05wnAlggo4SzZYZ40QSCJ78EpUzn96E+7t4StK9QjHI8hO2AHwMDWCBsMR9aWU8u548e7/ms/o2YTF0e5huk7SE7zaKrI/fDlARYIKHbUNrLFOo0fZ9tp0aWR7R9ivZydJzSiCMuTGFRqp5KBVggtGdZGpki+cVQ7gcwMPso1YPt7zMNrVOSPUxJjaszz7EGWCB1D6PoCZyVXGTsPIz0Vkm3PZisEJB+RerimlCCE8prcAdYILf8HxOxwVwds6KAwXCNta9aYHeqFc+7A8kLgItGmQhXCFggCAA4gbM54lbSlP0CCQ+0Xxzhqhi7lyqMdUNpQG1iOvoJWCBlKg/lHR7Q/tiC0GffuQ65zGcP6gdjvxx6pgUytNoRAgpYIJIrLr3TethAOk2L+H1niDFeh4n9S+lGsQ2TQ2W+L9mHC1gg4ysAT7MLuJGIjfa0aWmKlRvwegYldoRMrWPBa1oZp1UMWCBejb71fRUoVBycI/hb6+d9li8Nt/AGDr4cHqeFgqBNKg1YIEEWtB5rzkDXMcIL589lMjoSke6XEUextEsTR0FjpGfsDlggqhJ91EIJZrTW3ovq5Fc5yX3hl+24F0s2PM+kUXY4y0EPWCBwj8knItIZzL0BOLINHgHAIfh5pkXlftuhcEnFcxY9KxBYINfHE/iNIeoMvp5KXeNBZlfHhHIlEfvjGbsaAx1IwJN3EVggm43zZkO72fc1lwnX/8Q4O1C/BV2CMQBRlf11ZwgIp5RtZGV2aWNlS2V5SW5mb6FpZGV2aWNlS2V5pAECIAEhWCCIwikMm+n28zOyVBGiAr/UOuX81Zgv7i3PzdvwPzG5biJYIDj/fzDqOGd/zfMfj0+xRiMUMX0+Y2VIOVmS66zFm1XxZ2RvY1R5cGV2b3JnLmlzby4xODAxMy41LjEubURMCWx2YWxpZGl0eUluZm+janZhbGlkVW50aWzAdDIwMjQtMTAtMDJUMjI6Mjg6NDBaaXZhbGlkRnJvbcB0MjAyNC0wOS0wMlQyMjoyODo0MVpmc2lnbmVkwHQyMDI0LTA5LTAyVDIyOjI4OjQxWmlfYnJhbmRpbmemZG5hbWVuRHJpdmVyIExpY2Vuc2VrZGVzY3JpcHRpb254MERldGFpbHMgYWJvdXQgeW91ciBhZ2UsIGlkZW50aXR5ICYgbGljZW5zZSBjbGFzc29iYWNrZ3JvdW5kQ29sb3JnIzAwN0VBN253YXRlcm1hcmtJbWFnZaJmZm9ybWF0Y3N2Z2RkYXRhWS0FPHN2ZyB3aWR0aD0iMjQ1IiBoZWlnaHQ9IjE1MCIgdmlld0JveD0iMCAwIDI0NSAxNTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8xMjEzXzU1MjApIj4KPHBhdGggZD0iTTE1NS4wNjkgNjYuODY3NUwxNDAuNDgyIDU1LjA5NjlDMTQwLjA4MiA1NC43NzQ3IDEzOS44NDggNTQuMjg3NiAxMzkuODQ4IDUzLjc3MjdMMTM5Ljg0OCA0NC42NDc5QzEzOS44NDggNDMuOTg1OCAxNDAuMjM0IDQzLjM4MiAxNDAuODM1IDQzLjEwM0wxNTMuMDQ1IDM3LjQ0MDlDMTUzLjkyNyAzNy4wMzI1IDE1NC4yODggMzUuOTY3IDE1My44MzYgMzUuMTA0NUwxNDkuNzgyIDI3LjM4NTFDMTQ5LjY1MyAyNy4xNDE2IDE0OS41ODcgMjYuODcwMiAxNDkuNTg3IDI2LjU5MzdMMTQ5LjU4NyAyMi41NDVDMTQ5LjU4NyAyMS45NDM3IDE0OS45MDcgMjEuMzg1NyAxNTAuNDI0IDIxLjA3ODdMMTc1LjM3NiA2LjM1MjcyQzE3NS45ODcgNS45OTI1IDE3Ni43NTggNi4wNDgzMSAxNzcuMzExIDYuNDk0NzhMMjAwLjk2NyAyNS42MDE4TDIwMC45NjcgMzguNzA2OEMyMDAuOTY3IDM5LjY0NzkgMjAwLjIwNiA0MC40MDg5IDE5OS4yNjUgNDAuNDA4OUwxODUuODIgNDEuMjQ1M0MxODQuODc5IDQxLjI0NTMgMTg0LjExOCA0Mi4wMDYzIDE4NC4xMTggNDIuOTQ3NUwxODMuMDA5IDU1LjU5NDFDMTgzLjAwOSA1Ni41MzUyIDE4My43NyA1Ny4yOTYyIDE4NC43MTEgNTcuMjk2MkMxODUuNjUyIDU3LjI5NjIgMTg2LjQxMyA1OC4wNTczIDE4Ni40MTMgNTguOTk4NEwxODYuNDEzIDcwLjUyNTVDMTg2LjQxMyA3MS40NjY3IDE4NS42NTIgNzIuMjI3NyAxODQuNzExIDcyLjIyNzdMMTczLjE3NiA3Mi4yMjc3QzE3My4wMTIgNzIuMjI3NyAxNzIuODQ3IDcyLjIwMjMgMTcyLjY4NyA3Mi4xNTY3TDE1NS4wNjYgNjYuODY3NSIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTE5Mi44OCA1OC41MjIxTDE5Ni41OTYgNTQuMzMyNEMxOTYuNzY3IDU0LjEzOTYgMTk2Ljg0MyA1My44ODExIDE5Ni44MDUgNTMuNjI2M0wxOTUuNjYxIDQ2LjAxODhDMTk1LjYxIDQ1LjY3NjkgMTk1LjM2MSA0NS4zOTc5IDE5NS4wMjcgNDUuMzA4NUwxOTEuMjk5IDQ0LjMwOTdDMTkwLjgzNSA0NC4xODU0IDE5MC4zNTkgNDQuNDYwNiAxOTAuMjM0IDQ0LjkyNDNMMTg3Ljc4NCA1NC4wNjk2QzE4Ny42NiA1NC41MzMzIDE4Ny45MzUgNTUuMDEgMTg4LjM5OSA1NS4xMzQyTDE5MC4zODYgNTUuNjY2N0MxOTAuNzA0IDU1Ljc1MTkgMTkwLjk0NyA1Ni4wMDk1IDE5MS4wMTMgNTYuMzMyTDE5MS4zNzggNTguMTE5NkMxOTEuNTIyIDU4LjgyMTQgMTkyLjQwNSA1OS4wNTggMTkyLjg4IDU4LjUyMjFaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTI2LjAxNiA5Ni4wMDk3TDEyNi40NjggODIuMjYyNUgxMzAuNTM0TDEzMy4xMTYgOTEuMDA3N0gxMzMuMjU2TDEzNS44MjcgODIuMjYyNUgxMzkuODgyTDE0MC4zNDQgOTYuMDA5N0gxMzcuNjEyTDEzNy40ODMgOTEuMDYxNUwxMzcuMzY1IDg1LjM0OTdIMTM3LjE5M0wxMzQuNDkzIDk0LjQwNjlIMTMxLjg2OEwxMjkuMTU3IDg1LjM0OTdIMTI4Ljk4NUwxMjguODY3IDkxLjA3MjNMMTI4Ljc0OSA5Ni4wMDk3SDEyNi4wMTZaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTQ4LjM0NiA5Ni4zMDAxQzE0Ni43MTEgOTYuMzAwMSAxNDUuNDcgOTUuODgwNiAxNDQuNjI0IDk1LjA0MTVDMTQzLjc4NSA5NC4yMDI1IDE0My4zNjUgOTMuMDA4NSAxNDMuMzY1IDkxLjQ1OTVWOTAuMDI4OUMxNDMuMzY1IDg4LjQ3MjcgMTQzLjc4NSA4Ny4yNzUxIDE0NC42MjQgODYuNDM2MUMxNDUuNDcgODUuNTg5OSAxNDYuNzExIDg1LjE2NjggMTQ4LjM0NiA4NS4xNjY4QzE0OS45NzMgODUuMTY2OCAxNTEuMjA3IDg1LjU4OTkgMTUyLjA0NiA4Ni40MzYxQzE1Mi44ODUgODcuMjc1MSAxNTMuMzA1IDg4LjQ3MjcgMTUzLjMwNSA5MC4wMjg5VjkxLjQ1OTVDMTUzLjMwNSA5My4wMDg1IDE1Mi44ODUgOTQuMjAyNSAxNTIuMDQ2IDk1LjA0MTVDMTUxLjIxNCA5NS44ODA2IDE0OS45ODEgOTYuMzAwMSAxNDguMzQ2IDk2LjMwMDFaTTE0OC4zNDYgOTQuMDk1QzE0OS4wNjMgOTQuMDk1IDE0OS42MDggOTMuODc5OCAxNDkuOTgxIDkzLjQ0OTVDMTUwLjM2MSA5My4wMTkzIDE1MC41NTEgOTIuNDAyNSAxNTAuNTUxIDkxLjU5OTRWODkuODg5QzE1MC41NTEgODkuMDcxNSAxNTAuMzYxIDg4LjQ0NzYgMTQ5Ljk4MSA4OC4wMTc0QzE0OS42MDggODcuNTc5OSAxNDkuMDYzIDg3LjM2MTIgMTQ4LjM0NiA4Ny4zNjEyQzE0Ny42MjEgODcuMzYxMiAxNDcuMDY5IDg3LjU3OTkgMTQ2LjY4OSA4OC4wMTc0QzE0Ni4zMTYgODguNDQ3NiAxNDYuMTMgODkuMDcxNSAxNDYuMTMgODkuODg5VjkxLjU5OTRDMTQ2LjEzIDkyLjQwMjUgMTQ2LjMxNiA5My4wMTkzIDE0Ni42ODkgOTMuNDQ5NUMxNDcuMDY5IDkzLjg3OTggMTQ3LjYyMSA5NC4wOTUgMTQ4LjM0NiA5NC4wOTVaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTYzLjI2OCA5Ni4wMDk3Vjg5LjY3MzlDMTYzLjI2OCA4OS4yMjkzIDE2My4yMDcgODguODQ1NiAxNjMuMDg1IDg4LjUyMjlDMTYyLjk3IDg4LjIwMDIgMTYyLjc3NiA4Ny45NDkyIDE2Mi41MDQgODcuNzdDMTYyLjIzMSA4Ny41OTA3IDE2MS44NTkgODcuNTAxIDE2MS4zODUgODcuNTAxQzE2MC45NjkgODcuNTAxIDE2MC42MDQgODcuNTc2MyAxNjAuMjg4IDg3LjcyNjlDMTU5Ljk4IDg3Ljg3NzUgMTU5LjcyNSA4OC4wODE5IDE1OS41MjQgODguMzQwMUMxNTkuMzMxIDg4LjU5MTEgMTU5LjE4NCA4OC44Nzc5IDE1OS4wODMgODkuMjAwNkwxNTguNjUzIDg3LjY5NDdIMTU5LjE2OUMxNTkuMjg0IDg3LjIyODUgMTU5LjQ3NCA4Ni44MDkgMTU5LjczOSA4Ni40MzYxQzE2MC4wMTIgODYuMDYzMiAxNjAuMzc4IDg1Ljc2OTIgMTYwLjgzNyA4NS41NTQxQzE2MS4zMDMgODUuMzMxNyAxNjEuODg0IDg1LjIyMDYgMTYyLjU3OSA4NS4yMjA2QzE2My4zOSA4NS4yMjA2IDE2NC4wNDYgODUuMzc0OCAxNjQuNTQ4IDg1LjY4MzFDMTY1LjA1IDg1Ljk4NDMgMTY1LjQxOSA4Ni40MzYxIDE2NS42NTYgODcuMDM4NUMxNjUuODk5IDg3LjY0MDkgMTY2LjAyMSA4OC4zODY3IDE2Ni4wMjEgODkuMjc1OVY5Ni4wMDk3SDE2My4yNjhaTTE1Ni4zOTQgOTYuMDA5N1Y4NS40NTcySDE1OS4xNDhMMTU5LjA0IDg4LjAyODFMMTU5LjE0OCA4OC4yNTRWOTYuMDA5N0gxNTYuMzk0WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTE3My40NDkgOTYuMjM1NkMxNzIuNTUyIDk2LjIzNTYgMTcxLjgzNSA5Ni4xMDI5IDE3MS4yOTcgOTUuODM3NkMxNzAuNzY3IDk1LjU2NSAxNzAuMzgzIDk1LjE1NjMgMTcwLjE0NiA5NC42MTEzQzE2OS45MSA5NC4wNjYzIDE2OS43OTEgOTMuMzk1OCAxNjkuNzkxIDkyLjU5OThWODYuNDQ2OUgxNzIuNTI0VjkyLjE5MUMxNzIuNTI0IDkyLjc2NDcgMTcyLjY1MyA5My4xODc4IDE3Mi45MTEgOTMuNDYwM0MxNzMuMTc2IDkzLjcyNTYgMTczLjYzOSA5My44NTgzIDE3NC4yOTggOTMuODU4M0MxNzQuNjg2IDkzLjg1ODMgMTc1LjA1OSA5My44MTg5IDE3NS40MTcgOTMuNzRDMTc1Ljc3NiA5My42NTM5IDE3Ni4xMDYgOTMuNTQyOCAxNzYuNDA3IDkzLjQwNjVMMTc2LjE3IDk1LjcwODVDMTc1LjgxMiA5NS44NzM0IDE3NS4zOTkgOTYuMDAyNSAxNzQuOTMzIDk2LjA5NTdDMTc0LjQ3NCA5Ni4xODg5IDE3My45NzkgOTYuMjM1NiAxNzMuNDQ5IDk2LjIzNTZaTTE2OC4yNjQgODcuNzE2MlY4NS41NDMzSDE3Ni4zMUwxNzYuMDczIDg3LjcxNjJIMTY4LjI2NFpNMTY5LjgyNCA4NS43NDc3TDE2OS44MTMgODIuOTUwOUwxNzIuNTU2IDgyLjY3MTJMMTcyLjQ0OCA4NS43NDc3SDE2OS44MjRaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTgzLjM2NiA5Ni4yNzg2QzE4MS43MzggOTYuMjc4NiAxODAuNTMgOTUuODQ4MyAxNzkuNzQxIDk0Ljk4NzhDMTc4Ljk1OSA5NC4xMjcyIDE3OC41NjkgOTIuOTE4OSAxNzguNTY5IDkxLjM2MjdWOTAuMDcxOUMxNzguNTY5IDg4LjUyMjkgMTc4Ljk2MyA4Ny4zMjE4IDE3OS43NTIgODYuNDY4NEMxODAuNTQxIDg1LjYxNSAxODEuNzQ1IDg1LjE4ODMgMTgzLjM2NiA4NS4xODgzQzE4My43ODkgODUuMTg4MyAxODQuMTg0IDg1LjIyNDIgMTg0LjU0OSA4NS4yOTU5QzE4NC45MjIgODUuMzYwNCAxODUuMjU5IDg1LjQ1MDEgMTg1LjU2IDg1LjU2NDhDMTg1Ljg2OSA4NS42Nzk1IDE4Ni4xMzggODUuODAxNSAxODYuMzY3IDg1LjkzMDVMMTg2LjU5MyA4OC4yNDMzQzE4Ni4yNDIgODguMDIwOSAxODUuODQ3IDg3LjgzNDUgMTg1LjQxIDg3LjY4MzlDMTg0Ljk4IDg3LjUzMzMgMTg0LjQ4MSA4Ny40NTggMTgzLjkxNSA4Ny40NThDMTgzLjAyNSA4Ny40NTggMTgyLjM3MyA4Ny42ODc1IDE4MS45NTcgODguMTQ2NEMxODEuNTQ4IDg4LjU5ODIgMTgxLjM0NCA4OS4yNTggMTgxLjM0NCA5MC4xMjU3VjkxLjI2NTlDMTgxLjM0NCA5Mi4xMjY1IDE4MS41NTUgOTIuNzg5OCAxODEuOTc4IDkzLjI1NTlDMTgyLjQwOSA5My43MjIgMTgzLjA2OCA5My45NTUxIDE4My45NTggOTMuOTU1MUMxODQuNTI0IDkzLjk1NTEgMTg1LjAyNiA5My44ODM0IDE4NS40NjQgOTMuNzRDMTg1LjkwMSA5My41ODk0IDE4Ni4zMSA5My40MDY1IDE4Ni42OSA5My4xOTE0TDE4Ni40NjQgOTUuNTE0OEMxODYuMTEzIDk1LjcxNTYgMTg1LjY3MiA5NS44OTEzIDE4NS4xNDEgOTYuMDQxOUMxODQuNjEgOTYuMTk5NyAxODQuMDE5IDk2LjI3ODYgMTgzLjM2NiA5Ni4yNzg2WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTE4OS41ODEgOTYuMDA5N1Y4MS43ODkySDE5Mi4zNDVWOTYuMDA5N0gxODkuNTgxWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTE5NS44ODggOTYuMDA5N1Y4NS40NTcySDE5OC42NDJWOTYuMDA5N0gxOTUuODg4Wk0xOTcuMjY1IDg0LjIwOTVDMTk2Ljc0MSA4NC4yMDk1IDE5Ni4zNTQgODQuMDg3NSAxOTYuMTAzIDgzLjg0MzdDMTk1Ljg1OSA4My41OTI3IDE5NS43MzcgODMuMjQ4NSAxOTUuNzM3IDgyLjgxMTFWODIuNzU3M0MxOTUuNzM3IDgyLjMxOTggMTk1Ljg1OSA4MS45NzU2IDE5Ni4xMDMgODEuNzI0NkMxOTYuMzU0IDgxLjQ3MzYgMTk2Ljc0MSA4MS4zNDgxIDE5Ny4yNjUgODEuMzQ4MUMxOTcuNzgxIDgxLjM0ODEgMTk4LjE2NSA4MS40NzM2IDE5OC40MTYgODEuNzI0NkMxOTguNjY3IDgxLjk3NTYgMTk4Ljc5MiA4Mi4zMTk4IDE5OC43OTIgODIuNzU3M1Y4Mi44MTExQzE5OC43OTIgODMuMjU1NyAxOTguNjY3IDgzLjU5OTkgMTk4LjQxNiA4My44NDM3QzE5OC4xNjUgODQuMDg3NSAxOTcuNzgxIDg0LjIwOTUgMTk3LjI2NSA4NC4yMDk1WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTIwNS44NDMgODEuNDU1N0MyMDYuNDAyIDgxLjQ1NTcgMjA2LjkxOCA4MS41MDIzIDIwNy4zOTIgODEuNTk1NkMyMDcuODY1IDgxLjY4ODggMjA4LjI4NCA4MS44MDM1IDIwOC42NSA4MS45Mzk4TDIwOC44OTcgODQuMDA1MUMyMDguNTg5IDgzLjkxMTggMjA4LjI2NiA4My44MzY1IDIwNy45MjkgODMuNzc5MkMyMDcuNTk5IDgzLjcxNDYgMjA3LjIzNCA4My42ODI0IDIwNi44MzIgODMuNjgyNEMyMDYuMzUyIDgzLjY4MjQgMjA1Ljk3MiA4My43MzYyIDIwNS42OTIgODMuODQzN0MyMDUuNDE5IDgzLjk1MTMgMjA1LjIyNiA4NC4xMDU1IDIwNS4xMTEgODQuMzA2M0MyMDQuOTk2IDg0LjUwNzEgMjA0LjkzOSA4NC43NDM3IDIwNC45MzkgODUuMDE2MlY4NS4wNDg1QzIwNC45MzkgODUuMjQyMSAyMDQuOTY4IDg1LjQyNSAyMDUuMDI1IDg1LjU5NzFDMjA1LjA4MiA4NS43NjkyIDIwNS4xNTEgODUuOTIzNCAyMDUuMjI5IDg2LjA1OTZMMjAzLjQzMyA4Ni4xMjQyVjg1LjgzMzdDMjAzLjEwMyA4NS42NzYgMjAyLjgyMyA4NS40NDI5IDIwMi41OTQgODUuMTM0NUMyMDIuMzY1IDg0LjgyNjIgMjAyLjI1IDg0LjQ0MjUgMjAyLjI1IDgzLjk4MzZWODMuOTI5OEMyMDIuMjUgODMuMTY5NiAyMDIuNTQgODIuNTY3MiAyMDMuMTIxIDgyLjEyMjZDMjAzLjcwOSA4MS42NzggMjA0LjYxNiA4MS40NTU3IDIwNS44NDMgODEuNDU1N1pNMjAyLjYxNiA5Ni4wMDk3Vjg2LjY5NDNIMjA1LjM1OFY5Ni4wMDk3SDIwMi42MTZaTTIwMS4wODggODguMDkyN1Y4NS45MDlMMjAzLjc1NiA4NS45MzA1TDIwNC44MzEgODUuOTA5SDIwOC44NzZMMjA4LjYzOSA4OC4wOTI3SDIwMS4wODhaTTIxMi43NDggODEuNDU1N0MyMTMuMzA4IDgxLjQ1NTcgMjEzLjgyNCA4MS41MDIzIDIxNC4yOTcgODEuNTk1NkMyMTQuNzcxIDgxLjY4ODggMjE1LjE5IDgxLjgwMzUgMjE1LjU1NiA4MS45Mzk4TDIxNS44MDMgODQuMDA1MUMyMTUuNDk1IDgzLjkxMTggMjE1LjE3MiA4My44MzY1IDIxNC44MzUgODMuNzc5MkMyMTQuNTA1IDgzLjcxNDYgMjE0LjE0IDgzLjY4MjQgMjEzLjczOCA4My42ODI0QzIxMy4yNTggODMuNjgyNCAyMTIuODc3IDgzLjczNjIgMjEyLjU5OCA4My44NDM3QzIxMi4zMjUgODMuOTUxMyAyMTIuMTMyIDg0LjEwNTUgMjEyLjAxNyA4NC4zMDYzQzIxMS45MDIgODQuNTA3MSAyMTEuODQ1IDg0Ljc0MzcgMjExLjg0NSA4NS4wMTYyVjg1LjA0ODVDMjExLjg0NSA4NS4yNDIxIDIxMS44NzQgODUuNDI1IDIxMS45MzEgODUuNTk3MUMyMTEuOTg4IDg1Ljc2OTIgMjEyLjA1NiA4NS45MjM0IDIxMi4xMzUgODYuMDU5NkwyMTAuMzM5IDg2LjEyNDJWODUuODMzN0MyMTAuMDA5IDg1LjY3NiAyMDkuNzI5IDg1LjQ0MjkgMjA5LjUgODUuMTM0NUMyMDkuMjcgODQuODI2MiAyMDkuMTU2IDg0LjQ0MjUgMjA5LjE1NiA4My45ODM2VjgzLjkyOThDMjA5LjE1NiA4My4xNjk2IDIwOS40NDYgODIuNTY3MiAyMTAuMDI3IDgyLjEyMjZDMjEwLjYxNSA4MS42NzggMjExLjUyMiA4MS40NTU3IDIxMi43NDggODEuNDU1N1pNMjA5LjUyMSA5Ni4wMDk3Vjg2LjY5NDNIMjEyLjI2NFY5Ni4wMDk3SDIwOS41MjFaTTIwNy45OTQgODguMDkyN1Y4NS45MDlMMjEwLjY2MiA4NS45MzA1TDIxMS43MzcgODUuOTA5SDIxNS43ODJMMjE1LjU0NSA4OC4wOTI3SDIwNy45OTRaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTQ2LjY3NCAxMTkuNzQ0VjExNi4yNDZIMTUwLjM5OUMxNTEuNDY3IDExNi4yNDYgMTUyLjI2OCAxMTUuOTU3IDE1Mi44MDIgMTE1LjM3OEMxNTMuMzM2IDExNC44IDE1My42MDMgMTEzLjk3MiAxNTMuNjAzIDExMi44OTVWMTA5LjQ3N0MxNTMuNjAzIDEwOC40IDE1My4zMzYgMTA3LjU3NyAxNTIuODAyIDEwNy4wMDhDMTUyLjI2OCAxMDYuNDI5IDE1MS40NjcgMTA2LjE0IDE1MC4zOTkgMTA2LjE0SDE0Ni42NjFWMTAyLjY4MkgxNTAuNTQ2QzE1Mi45NzYgMTAyLjY4MiAxNTQuNzkxIDEwMy4yNyAxNTUuOTkzIDEwNC40NDRDMTU3LjIwMyAxMDUuNjEgMTU3LjgwOSAxMDcuMjg4IDE1Ny44MDkgMTA5LjQ3N1YxMTIuOTA4QzE1Ny44MDkgMTE1LjEwNyAxNTcuMjA4IDExNi43OTggMTU2LjAwNiAxMTcuOTgxQzE1NC44MDUgMTE5LjE1NiAxNTIuOTg1IDExOS43NDQgMTUwLjU0NiAxMTkuNzQ0SDE0Ni42NzRaTTE0My43NTEgMTE5Ljc0NFYxMDIuNjgySDE0Ny44NjNWMTE5Ljc0NEgxNDMuNzUxWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTE2MS40MyAxMTkuNzQ0TDE2MS45NjQgMTAyLjY4MkgxNjcuNzg1TDE3MC42MDIgMTEyLjg1NUgxNzAuNzc1TDE3My41OTIgMTAyLjY4MkgxNzkuNDEzTDE3OS45NDcgMTE5Ljc0NEgxNzUuOTAyTDE3NS43ODIgMTE0LjE1TDE3NS42NjIgMTA3LjM0MUgxNzUuNDYxTDE3Mi41MzggMTE3Ljc0MUgxNjguODI2TDE2NS45MDMgMTA3LjM0MUgxNjUuNjg5TDE2NS41ODIgMTE0LjE2M0wxNjUuNDc1IDExOS43NDRIMTYxLjQzWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTE4Ny4zMjEgMTE5Ljc0NEwxODIuNjQ5IDEwMi42ODJIMTg2Ljk3NEwxOTAuMzI1IDExNi42MzNIMTkwLjYzMkwxOTMuOTk2IDEwMi42ODJIMTk4LjMwOEwxOTMuNjQ5IDExOS43NDRIMTg3LjMyMVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0xOTguNzg0IC0xMS43MzYzTDE5NS42MTEgLTguNzA2MThMMTkxLjMxMSAtOS42MDcwM0wxOTMuMjE1IC01LjYzNTA4TDE5MS4wNDUgLTEuODI2OTJMMTk1LjM4NiAtMi40MjA2NkwxOTguMzU0IDAuODM0NzAxTDE5OS4xMzIgLTMuNDg1MzFMMjAzLjE0NSAtNS4yODcwMkwxOTkuMjc2IC03LjM3NTM3TDE5OC43ODQgLTExLjczNjNaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMjIzLjcwMSAyLjc2MTMzTDIxOS42NjggNC41NDI1NkwyMTUuOTQxIDIuMjI5TDIxNi4zNzEgNi41ODk5NkwyMTMuMDE0IDkuNDM1ODVMMjE3LjMxMyAxMC4zNzc3TDIxOC45NzIgMTQuNDMxNUwyMjEuMTgzIDEwLjY0MzhMMjI1LjU2NCAxMC4zMTYyTDIyMi42NTcgNy4wNDAzOUwyMjMuNzAxIDIuNzYxMzNaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMjQyLjEyNyAyNC45MTQyTDIzNy43NDYgMjUuMjAwOUwyMzUuMDIzIDIxLjc2MTJMMjMzLjkzOCAyNi4wMTk4TDIyOS44MjIgMjcuNTM0OUwyMzMuNTI4IDI5Ljg2ODlMMjMzLjcxMiAzNC4yNzA4TDIzNy4wOTEgMzEuNDY1OUwyNDEuMzA4IDMyLjY1MzRMMjM5LjY5MSAyOC41NzkxTDI0Mi4xMjcgMjQuOTE0MloiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0yNTEuODcyIDUyLjAzODlMMjQ3LjY3NSA1MC44MTA1TDI0Ni4yODMgNDYuNjMzOEwyNDMuODA2IDUwLjI1NzdMMjM5LjQyNCA1MC4yNzgyTDI0Mi4xMDYgNTMuNzU4N0wyNDAuNzc2IDU3LjkzNTRMMjQ0LjkxMSA1Ni40NjEzTDI0OC40NzQgNTkuMDIwNkwyNDguMzUxIDU0LjYzOTFMMjUxLjg3MiA1Mi4wMzg5WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTI1MS43NzEgODAuODQ1OUwyNDguMjI5IDc4LjI0NTdMMjQ4LjM3MyA3My44NjQzTDI0NC43OSA3Ni40MjM1TDI0MC42NTQgNzQuOTQ5NEwyNDIuMDA1IDc5LjEyNjFMMjM5LjMwMyA4Mi42MDY3TDI0My43MDUgODIuNjI3MUwyNDYuMTgyIDg2LjI1MUwyNDcuNTU0IDgyLjA3NDNMMjUxLjc3MSA4MC44NDU5WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTI0MS44MDEgMTA3Ljg5NUwyMzkuMzY1IDEwNC4yNTFMMjQxLjAwMiAxMDAuMTU2TDIzNi43NjQgMTAxLjM2NEwyMzMuMzg2IDk4LjUzODZMMjMzLjIyMiAxMDIuOTRMMjI5LjQ5NiAxMDUuMjc1TDIzMy42MzIgMTA2LjgxTDIzNC42OTYgMTExLjA2OUwyMzcuNDIgMTA3LjYwOUwyNDEuODAxIDEwNy44OTVaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMjIzLjIxMSAxMjkuOTA0TDIyMi4xNjYgMTI1LjY0NUwyMjUuMDc0IDEyMi4zNDlMMjIwLjY5MiAxMjIuMDIxTDIxOC40ODEgMTE4LjIzM0wyMTYuODIzIDEyMi4zMDhMMjEyLjUyMyAxMjMuMjI5TDIxNS44ODEgMTI2LjA3NUwyMTUuNDUxIDEzMC40NTZMMjE5LjE3NyAxMjguMTQzTDIyMy4yMTEgMTI5LjkwNFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0xOTguMTkxIDE0NC4yMTRMMTk4LjY2MSAxMzkuODUzTDIwMi41MzEgMTM3Ljc2NUwxOTguNTM5IDEzNS45NjNMMTk3Ljc0IDEzMS42NDNMMTk0Ljc5MiAxMzQuODk4TDE5MC40MzEgMTM0LjMwNUwxOTIuNjIyIDEzOC4xMTNMMTkwLjcxOCAxNDIuMDY0TDE5NS4wMTcgMTQxLjE4NEwxOTguMTkxIDE0NC4yMTRaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTY5Ljc5NCAxNDkuMTA2TDE3MS43MzkgMTQ1LjE3NUwxNzYuMDggMTQ0LjU0MUwxNzIuOTI3IDE0MS40NjlMMTczLjY4NCAxMzcuMTQ5TDE2OS43OTQgMTM5LjE5N0wxNjUuOTA0IDEzNy4xNDlMMTY2LjY0MSAxNDEuNDY5TDE2My40ODggMTQ0LjU0MUwxNjcuODQ5IDE0NS4xNzVMMTY5Ljc5NCAxNDkuMTA2WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTE0MS40MTggMTQzLjk4OUwxNDQuNTkxIDE0MC45NThMMTQ4Ljg5MSAxNDEuODU5TDE0Ni45ODcgMTM3Ljg4N0wxNDkuMTc3IDEzNC4wNzlMMTQ0LjgxNiAxMzQuNjczTDE0MS44NjggMTMxLjQxN0wxNDEuMDcgMTM1LjczN0wxMzcuMDc3IDEzNy41NkwxNDAuOTQ3IDEzOS42MjhMMTQxLjQxOCAxNDMuOTg5WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTExNi41MiAxMjkuNDk1TDEyMC41MzMgMTI3LjczNEwxMjQuMjc5IDEzMC4wMjdMMTIzLjgyOSAxMjUuNjY2TDEyNy4xODcgMTIyLjgyTDEyMi44ODcgMTIxLjg5OUwxMjEuMjI5IDExNy44MjVMMTE5LjAxOCAxMjEuNjEyTDExNC42MzYgMTIxLjk0TDExNy41NjQgMTI1LjIxNkwxMTYuNTIgMTI5LjQ5NVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik05OC4wNzQgMTA3LjM0TDEwMi40NTUgMTA3LjA1NEwxMDUuMTc4IDExMC41MTRMMTA2LjI2NCAxMDYuMjU1TDExMC4zNzkgMTA0LjcyTDEwNi42NzMgMTAyLjM4NkwxMDYuNTA5IDk3Ljk4MzlMMTAzLjEzMSAxMDAuNzg5TDk4Ljg5MjkgOTkuNjAxM0wxMDAuNTEgMTAzLjY5Nkw5OC4wNzQgMTA3LjM0WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTg4LjMyNzYgODAuMjEyMUw5Mi41NDUzIDgxLjQ2MUw5My45MTcgODUuNjE3Mkw5Ni4zOTQ0IDgxLjk5MzNMMTAwLjc5NiA4MS45NzI5TDk4LjA5MzcgNzguNDkyM0w5OS40NDUgNzQuMzE1Nkw5NS4zMDkzIDc1Ljc4OTdMOTEuNzI2MyA3My4yMzA1TDkxLjg2OTYgNzcuNjMyNEw4OC4zMjc2IDgwLjIxMjFaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNODguNDMwNyA1MS40MDU2TDkxLjk3MjcgNTQuMDA1OEw5MS44NDk4IDU4LjM4NzJMOTUuNDEyMyA1NS44MjhMOTkuNTQ4IDU3LjMwMjFMOTguMjE3MiA1My4xMjU0TDEwMC44OTkgNDkuNjQ0OUw5Ni40OTc0IDQ5LjYyNDRMOTQuMDQwNSA0Ni4wMDA1TDkyLjY0ODMgNTAuMTc3Mkw4OC40MzA3IDUxLjQwNTZaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNOTguMzk5OSAyNC4zNkwxMDAuODM2IDI4LjAwNDRMOTkuMjE4OSAzMi4wOTkyTDEwMy40MzcgMzAuOTExN0wxMDYuODE1IDMzLjcxNjZMMTA2Ljk5OSAyOS4zMTQ3TDExMC43MDUgMjYuOTgwN0wxMDYuNTg5IDI1LjQ0NTFMMTA1LjUwNCAyMS4xODY1TDEwMi43ODEgMjQuNjQ2Nkw5OC4zOTk5IDI0LjM2WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTExNy4wMTIgMi4zNTE2NkwxMTguMDU2IDYuNjMwNzJMMTE1LjEyOCA5LjkwNjU2TDExOS41MSAxMC4yMzQxTDEyMS43MjEgMTQuMDIxOEwxMjMuNCA5Ljk0NzUxTDEyNy42NzkgOS4wMjYxOEwxMjQuMzIxIDYuMTgwM0wxMjQuNzcyIDEuODE5MzRMMTIxLjAyNSA0LjExMjQyTDExNy4wMTIgMi4zNTE2NloiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0xNDIuMDMgLTExLjk2MTRMMTQxLjUzOSAtNy42MDA0N0wxMzcuNjY5IC01LjUxMjEyTDE0MS42ODIgLTMuNzEwNDFMMTQyLjQ2IDAuNjA5NjAzTDE0NS40MjkgLTIuNjQ1NzZMMTQ5Ljc3IC0yLjA1MjAxTDE0Ny41OTkgLTUuODYwMThMMTQ5LjUwMyAtOS44MTE2NkwxNDUuMjA0IC04LjkzMTI4TDE0Mi4wMyAtMTEuOTYxNFoiIGZpbGw9IndoaXRlIi8+CjwvZz4KPGRlZnM+CjxjbGlwUGF0aCBpZD0iY2xpcDBfMTIxM181NTIwIj4KPHJlY3Qgd2lkdGg9IjI0NSIgaGVpZ2h0PSIxNDguNzUiIGZpbGw9IndoaXRlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDAuMzU2NDQ1KSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgpqaXNzdWVySWNvbqJmZm9ybWF0Y3N2Z2RkYXRhWTGYPHN2ZyB3aWR0aD0iMzMiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMyAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE2LjY2NjUgMzJDMjUuNTAzMSAzMiAzMi42NjY1IDI0LjgzNjYgMzIuNjY2NSAxNkMzMi42NjY1IDcuMTYzNDQgMjUuNTAzMSAwIDE2LjY2NjUgMEM3LjgyOTk1IDAgMC42NjY1MDQgNy4xNjM0NCAwLjY2NjUwNCAxNkMwLjY2NjUwNCAyNC44MzY2IDcuODI5OTUgMzIgMTYuNjY2NSAzMloiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTE2LjYxNzMgMzAuMjY0NEMyNC40ODY4IDMwLjI2NDQgMzAuODY2MiAyMy44ODUgMzAuODY2MiAxNi4wMTU1QzMwLjg2NjIgOC4xNDYwNSAyNC40ODY4IDEuNzY2NiAxNi42MTczIDEuNzY2NkM4Ljc0Nzg2IDEuNzY2NiAyLjM2ODQxIDguMTQ2MDUgMi4zNjg0MSAxNi4wMTU1QzIuMzY4NDEgMjMuODg1IDguNzQ3ODYgMzAuMjY0NCAxNi42MTczIDMwLjI2NDRaIiBmaWxsPSIjMDAzNDU5Ii8+CjxwYXRoIGQ9Ik0xNi42MTczIDEwLjQwNzdMMjUuMzMyMyAxMC44OTQ1TDE2LjYxNzMgMTEuMzgxNEw3LjkwMjM0IDEwLjg5NDVMMTYuNjE3MyAxMC40MDc3WiIgZmlsbD0iIzBBNEI3NyIvPgo8cGF0aCBkPSJNMTYuNjE3MyAyMS40OTA3TDcuOTAyMjcgMjEuMDAzOUwxNi42MTczIDIwLjUxNzFMMjUuMzMyMyAyMS4wMDM5TDE2LjYxNzMgMjEuNDkwN1oiIGZpbGw9IiMwQTRCNzciLz4KPHBhdGggZD0iTTE2LjYxNzMgOC44MzkzNkwyMy42MDQ1IDkuMzI2MThMMTYuNjE3MyA5LjgxMzAxTDkuNjMwMTMgOS4zMjYxOEwxNi42MTczIDguODM5MzZaIiBmaWxsPSIjMEE0Qjc3Ii8+CjxwYXRoIGQ9Ik0xNi42MTczIDIzLjA1OTFMOS42MzAxNiAyMi41NzIzTDE2LjYxNzMgMjIuMDg1NEwyMy42MDQ1IDIyLjU3MjNMMTYuNjE3MyAyMy4wNTkxWiIgZmlsbD0iIzBBNEI3NyIvPgo8cGF0aCBkPSJNMTYuNjE3MyA3LjI3MUwyMS42MzcyIDcuNzU3ODJMMTYuNjE3MyA4LjI0NDY1TDExLjU5NzQgNy43NTc4MkwxNi42MTczIDcuMjcxWiIgZmlsbD0iIzBBNEI3NyIvPgo8cGF0aCBkPSJNMTYuNjE3MyAyNC42MjdMMTEuNTk3NCAyNC4xNDAxTDE2LjYxNzMgMjMuNjUzM0wyMS42MzcyIDI0LjE0MDFMMTYuNjE3MyAyNC42MjdaIiBmaWxsPSIjMEE0Qjc3Ii8+CjxwYXRoIGQ9Ik0zMC4yNDMyIDE1Ljc4MkMzMC4yNDMyIDIzLjMwNTMgMjQuMTQxMSAyOS40MDM3IDE2LjYxNzggMjkuNDAzN0M5LjA5NDU2IDI5LjQwMzcgMi45OTYwOSAyMy4zMDUzIDIuOTk2MDkgMTUuNzgyQzIuOTk2MDkgMTUuMjYxOSAzLjAyNTQgMTQuNzQ5MSAzLjA4NCAxNC4yNDM3QzMuODQ5NTEgMjEuMDQxNyA5LjYxNDY3IDI2LjMyMzQgMTYuNjE3OCAyNi4zMjM0QzIzLjYyMSAyNi4zMjM0IDI5LjM4OTggMjEuMDQxNyAzMC4xNTUzIDE0LjI0MzdDMzAuMjEzOSAxNC43NDkxIDMwLjI0MzIgMTUuMjYxOSAzMC4yNDMyIDE1Ljc4MloiIGZpbGw9IiMwMDJENEQiLz4KPHBhdGggZD0iTTE0LjE4NjkgMTYuMTQxMkwxMS44Mjg5IDE0LjIzODRDMTEuNzY0MSAxNC4xODYzIDExLjcyNjMgMTQuMTA3NiAxMS43MjYzIDE0LjAyNDNMMTEuNzI2MyAxMi41NDkyQzExLjcyNjMgMTIuNDQyMiAxMS43ODg3IDEyLjM0NDYgMTEuODg1OSAxMi4yOTk1TDEzLjg1OTYgMTEuMzg0MUMxNC4wMDI0IDExLjMxODEgMTQuMDYwNiAxMS4xNDU5IDEzLjk4NzYgMTEuMDA2NUwxMy4zMzIzIDkuNzU4NTRDMTMuMzExMyA5LjcxOTE3IDEzLjMwMDcgOS42NzUyOSAxMy4zMDA3IDkuNjMwNTlMMTMuMzAwNyA4Ljk3NjA5QzEzLjMwMDcgOC44Nzg4OSAxMy4zNTI0IDguNzg4NjcgMTMuNDM2IDguNzM5MDVMMTcuNDY5NyA2LjM1ODQ3QzE3LjU2ODUgNi4zMDAyMyAxNy42OTMyIDYuMzA5MjYgMTcuNzgyNiA2LjM4MTQzTDIxLjYwNjcgOS40NzAyNUwyMS42MDY3IDExLjU4ODhDMjEuNjA2NyAxMS43NDA5IDIxLjQ4MzcgMTEuODY0IDIxLjMzMTUgMTEuODY0TDE5LjE1ODEgMTEuOTk5MkMxOS4wMDU5IDExLjk5OTIgMTguODgyOSAxMi4xMjIyIDE4Ljg4MjkgMTIuMjc0M0wxOC43MDM2IDE0LjMxODhDMTguNzAzNiAxNC40NzA5IDE4LjgyNjcgMTQuNTkzOSAxOC45Nzg4IDE0LjU5MzlDMTkuMTMxIDE0LjU5MzkgMTkuMjU0IDE0LjcxNyAxOS4yNTQgMTQuODY5MUwxOS4yNTQgMTYuNzMyNkMxOS4yNTQgMTYuODg0NyAxOS4xMzEgMTcuMDA3NyAxOC45Nzg4IDE3LjAwNzdMMTcuMTE0MSAxNy4wMDc3QzE3LjA4NzUgMTcuMDA3NyAxNy4wNjA4IDE3LjAwMzYgMTcuMDM1IDE2Ljk5NjNMMTQuMTg2NSAxNi4xNDEyIiBmaWxsPSIjMDBBOEU4Ii8+CjxwYXRoIGQ9Ik0yMC4yOTk2IDE0Ljc5MjNMMjAuOTAwMyAxNC4xMTVDMjAuOTI3OSAxNC4wODM4IDIwLjk0MDMgMTQuMDQyIDIwLjkzNDEgMTQuMDAwOEwyMC43NDkyIDEyLjc3MUMyMC43NDA5IDEyLjcxNTcgMjAuNzAwNiAxMi42NzA2IDIwLjY0NjYgMTIuNjU2MkwyMC4wNDQgMTIuNDk0N0MxOS45NjkgMTIuNDc0NiAxOS44OTIgMTIuNTE5MSAxOS44NzE5IDEyLjU5NDFMMTkuNDc1OCAxNC4wNzI1QzE5LjQ1NTcgMTQuMTQ3NSAxOS41MDAyIDE0LjIyNDUgMTkuNTc1MSAxNC4yNDQ2TDE5Ljg5NjQgMTQuMzMwN0MxOS45NDc4IDE0LjM0NDUgMTkuOTg3IDE0LjM4NjEgMTkuOTk3NyAxNC40MzgyTDIwLjA1NjggMTQuNzI3MkMyMC4wOCAxNC44NDA3IDIwLjIyMjggMTQuODc4OSAyMC4yOTk2IDE0Ljc5MjNaIiBmaWxsPSIjMDBBOEU4Ii8+CjxwYXRoIGQ9Ik05LjQ5MDQxIDIxLjAwNzhMOS41NjM0NCAxOC43ODU1SDEwLjIyMDhMMTAuNjM4MSAyMC4xOTkySDEwLjY2MDdMMTEuMDc2MyAxOC43ODU1SDExLjczMTlMMTEuODA2NyAyMS4wMDc4SDExLjM2NUwxMS4zNDQxIDIwLjIwNzlMMTEuMzI1IDE5LjI4NDVIMTEuMjk3MkwxMC44NjA3IDIwLjc0ODdIMTAuNDM2NEw5Ljk5ODE4IDE5LjI4NDVIOS45NzAzNUw5Ljk1MTIzIDIwLjIwOTZMOS45MzIxIDIxLjAwNzhIOS40OTA0MVpNMTMuMTAwMSAyMS4wNTQ4QzEyLjgzNTggMjEuMDU0OCAxMi42MzUzIDIwLjk4NjkgMTIuNDk4NSAyMC44NTEzQzEyLjM2MjggMjAuNzE1NyAxMi4yOTUgMjAuNTIyNyAxMi4yOTUgMjAuMjcyMlYyMC4wNDFDMTIuMjk1IDE5Ljc4OTQgMTIuMzYyOCAxOS41OTU4IDEyLjQ5ODUgMTkuNDYwMkMxMi42MzUzIDE5LjMyMzQgMTIuODM1OCAxOS4yNTUgMTMuMTAwMSAxOS4yNTVDMTMuMzYzMyAxOS4yNTUgMTMuNTYyNyAxOS4zMjM0IDEzLjY5ODMgMTkuNDYwMkMxMy44MzQgMTkuNTk1OCAxMy45MDE4IDE5Ljc4OTQgMTMuOTAxOCAyMC4wNDFWMjAuMjcyMkMxMy45MDE4IDIwLjUyMjcgMTMuODM0IDIwLjcxNTcgMTMuNjk4MyAyMC44NTEzQzEzLjU2MzggMjAuOTg2OSAxMy4zNjQ0IDIxLjA1NDggMTMuMTAwMSAyMS4wNTQ4Wk0xMy4xMDAxIDIwLjY5ODNDMTMuMjE2MSAyMC42OTgzIDEzLjMwNDIgMjAuNjYzNSAxMy4zNjQ0IDIwLjU5MzlDMTMuNDI1OSAyMC41MjQ0IDEzLjQ1NjYgMjAuNDI0NyAxMy40NTY2IDIwLjI5NDlWMjAuMDE4NEMxMy40NTY2IDE5Ljg4NjIgMTMuNDI1OSAxOS43ODUzIDEzLjM2NDQgMTkuNzE1OEMxMy4zMDQyIDE5LjY0NTEgMTMuMjE2MSAxOS42MDk3IDEzLjEwMDEgMTkuNjA5N0MxMi45ODMgMTkuNjA5NyAxMi44OTM4IDE5LjY0NTEgMTIuODMyMyAxOS43MTU4QzEyLjc3MjEgMTkuNzg1MyAxMi43NDE5IDE5Ljg4NjIgMTIuNzQxOSAyMC4wMTg0VjIwLjI5NDlDMTIuNzQxOSAyMC40MjQ3IDEyLjc3MjEgMjAuNTI0NCAxMi44MzIzIDIwLjU5MzlDMTIuODkzOCAyMC42NjM1IDEyLjk4MyAyMC42OTgzIDEzLjEwMDEgMjAuNjk4M1pNMTUuNTEyNCAyMS4wMDc4VjE5Ljk4MzZDMTUuNTEyNCAxOS45MTE3IDE1LjUwMjYgMTkuODQ5NyAxNS40ODI4IDE5Ljc5NzVDMTUuNDY0MyAxOS43NDUzIDE1LjQzMyAxOS43MDQ4IDE1LjM4ODkgMTkuNjc1OEMxNS4zNDQ5IDE5LjY0NjggMTUuMjg0NiAxOS42MzIzIDE1LjIwODEgMTkuNjMyM0MxNS4xNDA5IDE5LjYzMjMgMTUuMDgxNyAxOS42NDQ1IDE1LjAzMDcgMTkuNjY4OEMxNC45ODA5IDE5LjY5MzIgMTQuOTM5NyAxOS43MjYyIDE0LjkwNzMgMTkuNzY4QzE0Ljg3NiAxOS44MDg1IDE0Ljg1MjIgMTkuODU0OSAxNC44MzYgMTkuOTA3MUwxNC43NjY0IDE5LjY2MzZIMTQuODQ5OUMxNC44Njg0IDE5LjU4ODMgMTQuODk5MSAxOS41MjA0IDE0Ljk0MiAxOS40NjAyQzE0Ljk4NjEgMTkuMzk5OSAxNS4wNDUyIDE5LjM1MjQgMTUuMTE5NCAxOS4zMTc2QzE1LjE5NDggMTkuMjgxNiAxNS4yODg3IDE5LjI2MzcgMTUuNDAxMSAxOS4yNjM3QzE1LjUzMjEgMTkuMjYzNyAxNS42MzgyIDE5LjI4ODYgMTUuNzE5MyAxOS4zMzg0QzE1LjgwMDUgMTkuMzg3MSAxNS44NjAyIDE5LjQ2MDIgMTUuODk4NSAxOS41NTc1QzE1LjkzNzkgMTkuNjU0OSAxNS45NTc2IDE5Ljc3NTUgMTUuOTU3NiAxOS45MTkyVjIxLjAwNzhIMTUuNTEyNFpNMTQuNDAxMiAyMS4wMDc4VjE5LjMwMTlIMTQuODQ2NEwxNC44MjkgMTkuNzE3NUwxNC44NDY0IDE5Ljc1NFYyMS4wMDc4SDE0LjQwMTJaTTE3LjE1ODMgMjEuMDQ0M0MxNy4wMTMzIDIxLjA0NDMgMTYuODk3NCAyMS4wMjI5IDE2LjgxMDUgMjAuOThDMTYuNzI0NyAyMC45MzU5IDE2LjY2MjcgMjAuODY5OSAxNi42MjQ0IDIwLjc4MThDMTYuNTg2MSAyMC42OTM2IDE2LjU2NyAyMC41ODUzIDE2LjU2NyAyMC40NTY2VjE5LjQ2MTlIMTcuMDA4N1YyMC4zOTA1QzE3LjAwODcgMjAuNDgzMiAxNy4wMjk2IDIwLjU1MTYgMTcuMDcxMyAyMC41OTU3QzE3LjExNDIgMjAuNjM4NiAxNy4xODkgMjAuNjYgMTcuMjk1NiAyMC42NkMxNy4zNTgyIDIwLjY2IDE3LjQxODUgMjAuNjUzNyAxNy40NzY1IDIwLjY0MDlDMTcuNTM0NCAyMC42MjcgMTcuNTg3OCAyMC42MDkgMTcuNjM2NSAyMC41ODdMMTcuNTk4MiAyMC45NTkxQzE3LjU0MDIgMjAuOTg1OCAxNy40NzM2IDIxLjAwNjcgMTcuMzk4MiAyMS4wMjE3QzE3LjMyNCAyMS4wMzY4IDE3LjI0NCAyMS4wNDQzIDE3LjE1ODMgMjEuMDQ0M1pNMTYuMzIwMSAxOS42NjcxVjE5LjMxNThIMTcuNjIwOEwxNy41ODI2IDE5LjY2NzFIMTYuMzIwMVpNMTYuNTcyMiAxOS4zNDg5TDE2LjU3MDUgMTguODk2OEwxNy4wMTM5IDE4Ljg1MTVMMTYuOTk2NSAxOS4zNDg5SDE2LjU3MjJaTTE4Ljc2MTUgMjEuMDUxM0MxOC40OTgzIDIxLjA1MTMgMTguMzAzIDIwLjk4MTcgMTguMTc1NSAyMC44NDI2QzE4LjA0OTEgMjAuNzAzNSAxNy45ODU5IDIwLjUwODIgMTcuOTg1OSAyMC4yNTY2VjIwLjA0NzlDMTcuOTg1OSAxOS43OTc1IDE4LjA0OTcgMTkuNjAzMyAxOC4xNzcyIDE5LjQ2NTRDMTguMzA0NyAxOS4zMjc0IDE4LjQ5OTUgMTkuMjU4NCAxOC43NjE1IDE5LjI1ODRDMTguODI5OSAxOS4yNTg0IDE4Ljg5MzcgMTkuMjY0MiAxOC45NTI4IDE5LjI3NThDMTkuMDEzMSAxOS4yODYzIDE5LjA2NzUgMTkuMzAwOCAxOS4xMTYyIDE5LjMxOTNDMTkuMTY2MSAxOS4zMzc5IDE5LjIwOTYgMTkuMzU3NiAxOS4yNDY3IDE5LjM3ODRMMTkuMjgzMiAxOS43NTIzQzE5LjIyNjQgMTkuNzE2NCAxOS4xNjI2IDE5LjY4NjIgMTkuMDkxOSAxOS42NjE5QzE5LjAyMjMgMTkuNjM3NSAxOC45NDE4IDE5LjYyNTQgMTguODUwMiAxOS42MjU0QzE4LjcwNjQgMTkuNjI1NCAxOC42MDA5IDE5LjY2MjUgMTguNTMzNyAxOS43MzY3QzE4LjQ2NzYgMTkuODA5NyAxOC40MzQ2IDE5LjkxNjMgMTguNDM0NiAyMC4wNTY2VjIwLjI0MDlDMTguNDM0NiAyMC4zODAxIDE4LjQ2ODggMjAuNDg3MyAxOC41MzcyIDIwLjU2MjZDMTguNjA2NyAyMC42MzggMTguNzEzNCAyMC42NzU3IDE4Ljg1NzEgMjAuNjc1N0MxOC45NDg3IDIwLjY3NTcgMTkuMDI5OSAyMC42NjQxIDE5LjEwMDYgMjAuNjQwOUMxOS4xNzEzIDIwLjYxNjYgMTkuMjM3NCAyMC41ODcgMTkuMjk4OCAyMC41NTIyTDE5LjI2MjMgMjAuOTI3OEMxOS4yMDU1IDIwLjk2MDMgMTkuMTM0MiAyMC45ODg3IDE5LjA0ODQgMjEuMDEzQzE4Ljk2MjYgMjEuMDM4NSAxOC44NjcgMjEuMDUxMyAxOC43NjE1IDIxLjA1MTNaTTE5Ljc2NjIgMjEuMDA3OFYxOC43MDg5SDIwLjIxMzFWMjEuMDA3OEgxOS43NjYyWk0yMC43ODU4IDIxLjAwNzhWMTkuMzAxOUgyMS4yMzA5VjIxLjAwNzhIMjAuNzg1OFpNMjEuMDA4NCAxOS4xMDAyQzIwLjkyMzcgMTkuMTAwMiAyMC44NjExIDE5LjA4MDUgMjAuODIwNSAxOS4wNDExQzIwLjc4MTEgMTkuMDAwNSAyMC43NjE0IDE4Ljk0NDkgMjAuNzYxNCAxOC44NzQxVjE4Ljg2NTRDMjAuNzYxNCAxOC43OTQ3IDIwLjc4MTEgMTguNzM5MSAyMC44MjA1IDE4LjY5ODVDMjAuODYxMSAxOC42NTc5IDIwLjkyMzcgMTguNjM3NiAyMS4wMDg0IDE4LjYzNzZDMjEuMDkxOCAxOC42Mzc2IDIxLjE1MzggMTguNjU3OSAyMS4xOTQ0IDE4LjY5ODVDMjEuMjM1IDE4LjczOTEgMjEuMjU1MyAxOC43OTQ3IDIxLjI1NTMgMTguODY1NFYxOC44NzQxQzIxLjI1NTMgMTguOTQ2IDIxLjIzNSAxOS4wMDE3IDIxLjE5NDQgMTkuMDQxMUMyMS4xNTM4IDE5LjA4MDUgMjEuMDkxOCAxOS4xMDAyIDIxLjAwODQgMTkuMTAwMlpNMjIuMzk1IDE4LjY1NUMyMi40ODU0IDE4LjY1NSAyMi41Njg5IDE4LjY2MjYgMjIuNjQ1NCAxOC42Nzc2QzIyLjcyMTkgMTguNjkyNyAyMi43ODk4IDE4LjcxMTMgMjIuODQ4OSAxOC43MzMzTDIyLjg4ODkgMTkuMDY3MkMyMi44MzkgMTkuMDUyMSAyMi43ODY5IDE5LjAzOTkgMjIuNzMyNCAxOS4wMzA2QzIyLjY3OSAxOS4wMjAyIDIyLjYxOTkgMTkuMDE1IDIyLjU1NSAxOS4wMTVDMjIuNDc3MyAxOS4wMTUgMjIuNDE1OSAxOS4wMjM3IDIyLjM3MDcgMTkuMDQxMUMyMi4zMjY2IDE5LjA1ODUgMjIuMjk1MyAxOS4wODM0IDIyLjI3NjggMTkuMTE1OUMyMi4yNTgyIDE5LjE0ODMgMjIuMjQ4OSAxOS4xODY2IDIyLjI0ODkgMTkuMjMwNlYxOS4yMzU4QzIyLjI0ODkgMTkuMjY3MSAyMi4yNTM2IDE5LjI5NjcgMjIuMjYyOSAxOS4zMjQ1QzIyLjI3MjEgMTkuMzUyNCAyMi4yODMxIDE5LjM3NzMgMjIuMjk1OSAxOS4zOTkzTDIyLjAwNTUgMTkuNDA5N1YxOS4zNjI4QzIxLjk1MjIgMTkuMzM3MyAyMS45MDcgMTkuMjk5NiAyMS44Njk5IDE5LjI0OThDMjEuODMyOCAxOS4xOTk5IDIxLjgxNDIgMTkuMTM3OSAyMS44MTQyIDE5LjA2MzdWMTkuMDU1QzIxLjgxNDIgMTguOTMyMSAyMS44NjEyIDE4LjgzNDcgMjEuOTU1MSAxOC43NjI5QzIyLjA1MDEgMTguNjkxIDIyLjE5NjggMTguNjU1IDIyLjM5NSAxOC42NTVaTTIxLjg3MzMgMjEuMDA3OFYxOS41MDE5SDIyLjMxNjhWMjEuMDA3OEgyMS44NzMzWk0yMS42MjY0IDE5LjcyOFYxOS4zNzVMMjIuMDU3NyAxOS4zNzg0TDIyLjIzMTYgMTkuMzc1SDIyLjg4NTRMMjIuODQ3MSAxOS43MjhIMjEuNjI2NFpNMjMuNTExNCAxOC42NTVDMjMuNjAxOCAxOC42NTUgMjMuNjg1MyAxOC42NjI2IDIzLjc2MTggMTguNjc3NkMyMy44MzgzIDE4LjY5MjcgMjMuOTA2MSAxOC43MTEzIDIzLjk2NTMgMTguNzMzM0wyNC4wMDUzIDE5LjA2NzJDMjMuOTU1NCAxOS4wNTIxIDIzLjkwMzIgMTkuMDM5OSAyMy44NDg4IDE5LjAzMDZDMjMuNzk1NCAxOS4wMjAyIDIzLjczNjMgMTkuMDE1IDIzLjY3MTQgMTkuMDE1QzIzLjU5MzcgMTkuMDE1IDIzLjUzMjMgMTkuMDIzNyAyMy40ODcxIDE5LjA0MTFDMjMuNDQzIDE5LjA1ODUgMjMuNDExNyAxOS4wODM0IDIzLjM5MzIgMTkuMTE1OUMyMy4zNzQ2IDE5LjE0ODMgMjMuMzY1MyAxOS4xODY2IDIzLjM2NTMgMTkuMjMwNlYxOS4yMzU4QzIzLjM2NTMgMTkuMjY3MSAyMy4zNyAxOS4yOTY3IDIzLjM3OTMgMTkuMzI0NUMyMy4zODg1IDE5LjM1MjQgMjMuMzk5NSAxOS4zNzczIDIzLjQxMjMgMTkuMzk5M0wyMy4xMjE5IDE5LjQwOTdWMTkuMzYyOEMyMy4wNjg2IDE5LjMzNzMgMjMuMDIzMyAxOS4yOTk2IDIyLjk4NjMgMTkuMjQ5OEMyMi45NDkyIDE5LjE5OTkgMjIuOTMwNiAxOS4xMzc5IDIyLjkzMDYgMTkuMDYzN1YxOS4wNTVDMjIuOTMwNiAxOC45MzIxIDIyLjk3NzYgMTguODM0NyAyMy4wNzE1IDE4Ljc2MjlDMjMuMTY2NSAxOC42OTEgMjMuMzEzMiAxOC42NTUgMjMuNTExNCAxOC42NTVaTTIyLjk4OTcgMjEuMDA3OFYxOS41MDE5SDIzLjQzMzJWMjEuMDA3OEgyMi45ODk3Wk0yMi43NDI4IDE5LjcyOFYxOS4zNzVMMjMuMTc0MSAxOS4zNzg0TDIzLjM0OCAxOS4zNzVIMjQuMDAxOEwyMy45NjM1IDE5LjcyOEgyMi43NDI4WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTEyLjgyOTkgMjQuODQ0MlYyNC4yNzg4SDEzLjQzMkMxMy42MDQ2IDI0LjI3ODggMTMuNzM0MSAyNC4yMzIgMTMuODIwNSAyNC4xMzg1QzEzLjkwNjggMjQuMDQ1IDEzLjk1IDIzLjkxMTIgMTMuOTUgMjMuNzM3MVYyMy4xODQ2QzEzLjk1IDIzLjAxMDUgMTMuOTA2OCAyMi44Nzc0IDEzLjgyMDUgMjIuNzg1M0MxMy43MzQxIDIyLjY5MTggMTMuNjA0NiAyMi42NDUxIDEzLjQzMiAyMi42NDUxSDEyLjgyNzdWMjIuMDg2MUgxMy40NTU3QzEzLjg0ODUgMjIuMDg2MSAxNC4xNDIgMjIuMTgxIDE0LjMzNjMgMjIuMzcxQzE0LjUzMTkgMjIuNTU5NCAxNC42Mjk4IDIyLjgzMDcgMTQuNjI5OCAyMy4xODQ2VjIzLjczOTJDMTQuNjI5OCAyNC4wOTQ2IDE0LjUzMjcgMjQuMzY4IDE0LjMzODQgMjQuNTU5NEMxNC4xNDQyIDI0Ljc0OTMgMTMuODUgMjQuODQ0MiAxMy40NTU3IDI0Ljg0NDJIMTIuODI5OVpNMTIuMzU3MiAyNC44NDQyVjIyLjA4NjFIMTMuMDIxOVYyNC44NDQySDEyLjM1NzJaTTE1LjIxNTMgMjQuODQ0MkwxNS4zMDE2IDIyLjA4NjFIMTYuMjQyNkwxNi42OTc5IDIzLjczMDZIMTYuNzI2TDE3LjE4MTQgMjIuMDg2MUgxOC4xMjIzTDE4LjIwODcgMjQuODQ0MkgxNy41NTQ3TDE3LjUzNTMgMjMuOTRMMTcuNTE1OSAyMi44MzkzSDE3LjQ4MzVMMTcuMDEwOSAyNC41MjA1SDE2LjQxMDlMMTUuOTM4MiAyMi44MzkzSDE1LjkwMzdMMTUuODg2NSAyMy45NDIxTDE1Ljg2OTIgMjQuODQ0MkgxNS4yMTUzWk0xOS40MDA3IDI0Ljg0NDJMMTguNjQ1NCAyMi4wODYxSDE5LjM0NDZMMTkuODg2MyAyNC4zNDE0SDE5LjkzNkwyMC40Nzk4IDIyLjA4NjFIMjEuMTc2OUwyMC40MjM3IDI0Ljg0NDJIMTkuNDAwN1oiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTE2LjY2OTcgMi43MjMxNEwxNi4zNTgxIDMuMzUyODNMMTUuNjYyOCAzLjQ1NDVMMTYuMTY0NiAzLjk0NjQ0TDE2LjA0NjYgNC42NDE3M0wxNi42Njk3IDQuMzEzNzZMMTcuMjkyOCA0LjY0MTczTDE3LjE3NDcgMy45NDY0NEwxNy42NzY1IDMuNDU0NUwxNi45ODEzIDMuMzUyODNMMTYuNjY5NyAyLjcyMzE0WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMjEuMjExOSAzLjU0MzQ2TDIwLjcwMzUgNC4wMjg4NEwyMC4wMTQ4IDMuODg0NTRMMjAuMzE5OCA0LjUyMDc5TDE5Ljk3MjIgNS4xMzA4TDIwLjY2NzQgNS4wMzU2OUwyMS4xNDMgNS41NTcxNUwyMS4yNjc2IDQuODY1MTVMMjEuOTEwNCA0LjU3NjU0TDIxLjI5MDYgNC4yNDIwMkwyMS4yMTE5IDMuNTQzNDZaIiBmaWxsPSIjREM4NTQ4Ii8+CjxwYXRoIGQ9Ik0yNS4yMDMyIDUuODY1NTRMMjQuNTU3MSA2LjE1MDg3TDIzLjk2MDIgNS43ODAyN0wyNC4wMjkxIDYuNDc4ODNMMjMuNDkxMiA2LjkzNDdMMjQuMTc5OSA3LjA4NTU2TDI0LjQ0NTYgNy43MzQ5M0wyNC43OTk4IDcuMTI4MkwyNS41MDE2IDcuMDc1NzNMMjUuMDM1OSA2LjU1MDk5TDI1LjIwMzIgNS44NjU1NFoiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTI4LjE1NDkgOS40MTM3NUwyNy40NTMxIDkuNDU5NjdMMjcuMDE2OSA4LjkwODY5TDI2Ljg0MyA5LjU5MDg1TDI2LjE4MzggOS44MzM1NUwyNi43Nzc0IDEwLjIwNzRMMjYuODA3IDEwLjkxMjVMMjcuMzQ4MSAxMC40NjMyTDI4LjAyMzcgMTAuNjUzNUwyNy43NjQ2IDEwLjAwMDhMMjguMTU0OSA5LjQxMzc1WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMjkuNzE1OSAxMy43NTk0TDI5LjA0MzYgMTMuNTYyNkwyOC44MjA2IDEyLjg5MzZMMjguNDIzOCAxMy40NzRMMjcuNzIxOSAxMy40NzczTDI4LjE1MTYgMTQuMDM0OUwyNy45Mzg0IDE0LjcwMzlMMjguNjAwOSAxNC40Njc4TDI5LjE3MTUgMTQuODc3N0wyOS4xNTE4IDE0LjE3NTlMMjkuNzE1OSAxMy43NTk0WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMjkuNjk5NyAxOC4zNzM3TDI5LjEzMjMgMTcuOTU3MkwyOS4xNTUzIDE3LjI1NTRMMjguNTgxMyAxNy42NjUzTDI3LjkxODggMTcuNDI5MkwyOC4xMzUzIDE4LjA5ODJMMjcuNzAyNCAxOC42NTU4TDI4LjQwNzUgMTguNjU5MUwyOC44MDQzIDE5LjIzOTVMMjkuMDI0MSAxOC41NzA1TDI5LjY5OTcgMTguMzczN1oiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTI4LjEwMjQgMjIuNzA2M0wyNy43MTIxIDIyLjEyMjVMMjcuOTc0NSAyMS40NjY2TDI3LjI5NTYgMjEuNjYwMUwyNi43NTQ1IDIxLjIwNzVMMjYuNzI4MiAyMS45MTI2TDI2LjEzMTMgMjIuMjg2NUwyNi43OTM4IDIyLjUzMjVMMjYuOTY0NCAyMy4yMTQ2TDI3LjQwMDYgMjIuNjYwNEwyOC4xMDI0IDIyLjcwNjNaIiBmaWxsPSIjREM4NTQ4Ii8+CjxwYXRoIGQ9Ik0yNS4xMjQ2IDI2LjIzMTdMMjQuOTU3MyAyNS41NDk1TDI1LjQyMyAyNS4wMjE1TDI0LjcyMTIgMjQuOTY5TDI0LjM2NyAyNC4zNjIzTDI0LjEwMTMgMjUuMDE0OUwyMy40MTI2IDI1LjE2MjVMMjMuOTUwNSAyNS42MTg0TDIzLjg4MTYgMjYuMzIwMkwyNC40Nzg1IDI1Ljk0OTZMMjUuMTI0NiAyNi4yMzE3WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMjEuMTE3IDI4LjUyNDRMMjEuMTkyNCAyNy44MjU5TDIxLjgxMjMgMjcuNDkxM0wyMS4xNzI4IDI3LjIwMjdMMjEuMDQ0OCAyNi41MTA3TDIwLjU3MjYgMjcuMDMyMkwxOS44NzQgMjYuOTM3MUwyMC4yMjQ5IDI3LjU0NzFMMTkuOTE5OSAyOC4xODAxTDIwLjYwODcgMjguMDM5TDIxLjExNyAyOC41MjQ0WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMTYuNTY4IDI5LjMwNzlMMTYuODc5NiAyOC42NzgyTDE3LjU3NDggMjguNTc2NUwxNy4wNjk4IDI4LjA4NDZMMTcuMTkxMSAyNy4zOTI2TDE2LjU2OCAyNy43MjA1TDE1Ljk0NDkgMjcuMzkyNkwxNi4wNjI5IDI4LjA4NDZMMTUuNTU3OSAyOC41NzY1TDE2LjI1NjQgMjguNjc4MkwxNi41NjggMjkuMzA3OVoiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTEyLjAyMjQgMjguNDg4M0wxMi41MzA4IDI4LjAwMjlMMTMuMjE5NSAyOC4xNDcyTDEyLjkxNDUgMjcuNTExTDEzLjI2NTQgMjYuOTAxTDEyLjU2NjggMjYuOTk2MUwxMi4wOTQ2IDI2LjQ3NDZMMTEuOTY2NyAyNy4xNjY2TDExLjMyNzEgMjcuNDU4NUwxMS45NDcgMjcuNzg5N0wxMi4wMjI0IDI4LjQ4ODNaIiBmaWxsPSIjREM4NTQ4Ii8+CjxwYXRoIGQ9Ik04LjAzNDM5IDI2LjE2NjNMOC42NzcyIDI1Ljg4NDJMOS4yNzczNyAyNi4yNTE1TDkuMjA1MjIgMjUuNTUzTDkuNzQzMDggMjUuMDk3MUw5LjA1NDM1IDI0Ljk0OTVMOC43ODg3IDI0LjI5NjlMOC40MzQ1MSAyNC45MDM2TDcuNzMyNjcgMjQuOTU2MUw4LjIwMTY1IDI1LjQ4MDhMOC4wMzQzOSAyNi4xNjYzWiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNNS4wNzk1OSAyMi42MTc0TDUuNzgxNDMgMjIuNTcxNUw2LjIxNzYyIDIzLjEyNThMNi4zOTE0NCAyMi40NDM2TDcuMDUwNjQgMjIuMTk3Nkw2LjQ1NzAzIDIxLjgyMzhMNi40MzA3OSAyMS4xMTg3TDUuODg5NjYgMjEuNTY4TDUuMjEwNzcgMjEuMzc3N0w1LjQ2OTg2IDIyLjAzMzdMNS4wNzk1OSAyMi42MTc0WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMy41MTgzMSAxOC4yNzIyTDQuMTkzOTEgMTguNDcyMkw0LjQxMzY1IDE5LjEzOEw0LjgxMDQ4IDE4LjU1NzVMNS41MTU2IDE4LjU1NDJMNS4wODI2OSAxNy45OTY3TDUuMjk5MTUgMTcuMzI3Nkw0LjYzNjY2IDE3LjU2MzhMNC4wNjI3MyAxNy4xNTM4TDQuMDg1NjkgMTcuODU4OUwzLjUxODMxIDE4LjI3MjJaIiBmaWxsPSIjREM4NTQ4Ii8+CjxwYXRoIGQ9Ik0zLjUzNDkxIDEzLjY1NzhMNC4xMDIyOSAxNC4wNzQzTDQuMDgyNjEgMTQuNzc2Mkw0LjY1MzI2IDE0LjM2NjJMNS4zMTU3NSAxNC42MDIzTDUuMTAyNTcgMTMuOTMzM0w1LjUzMjIgMTMuMzc1OEw0LjgyNzA4IDEzLjM3MjVMNC40MzM1MyAxMi43OTJMNC4yMTA1MSAxMy40NjFMMy41MzQ5MSAxMy42NTc4WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNNS4xMzIwOCA5LjMyNTI0TDUuNTIyMzYgOS45MDkwMUw1LjI2MzI3IDEwLjU2NDlMNS45Mzg4NyAxMC4zNzQ3TDYuNDgwMDEgMTAuODI0TDYuNTA5NTIgMTAuMTE4OUw3LjEwMzEzIDkuNzQ1MDNMNi40NDM5MyA5LjQ5OTA2TDYuMjcwMTEgOC44MTY4OUw1LjgzMzkyIDkuMzcxMTVMNS4xMzIwOCA5LjMyNTI0WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNOC4xMTMyNSA1Ljc5OTYzTDguMjgwNTEgNi40ODUwN0w3LjgxMTUyIDcuMDA5ODFMOC41MTMzNiA3LjA2MjI4TDguODY3NTYgNy42NjkwMUw5LjEzNjQ5IDcuMDE2MzdMOS44MjE5MyA2Ljg2ODc4TDkuMjg0MDcgNi40MTI5Mkw5LjM1NjIzIDUuNzE0MzZMOC43NTYwNiA2LjA4MTY3TDguMTEzMjUgNS43OTk2M1oiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTEyLjEyMDkgMy41MDczMkwxMi4wNDIyIDQuMjA1ODhMMTEuNDIyNCA0LjU0MDQxTDEyLjA2NTIgNC44MjkwMUwxMi4xODk4IDUuNTIxMDFMMTIuNjY1MyA0Ljk5OTU1TDEzLjM2MDYgNS4wOTQ2NkwxMy4wMTMgNC40ODQ2NUwxMy4zMTggMy44NTE2OEwxMi42MjkzIDMuOTkyNzFMMTIuMTIwOSAzLjUwNzMyWiIgZmlsbD0iI0RDODU0OCIvPgo8L3N2Zz4Kamlzc3VlckxvZ2+iZmZvcm1hdGNzdmdkZGF0YVmBojxzdmcgd2lkdGg9IjE0NSIgaGVpZ2h0PSI0MiIgdmlld0JveD0iMCAwIDE0NSA0MiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTIwLjk2OTkgNDEuOTdDMzIuNTUxMiA0MS45NyA0MS45Mzk3IDMyLjU4MTUgNDEuOTM5NyAyMS4wMDAxQzQxLjkzOTcgOS40MTg4IDMyLjU1MTIgMC4wMzAyNzM0IDIwLjk2OTkgMC4wMzAyNzM0QzkuMzg4NTMgMC4wMzAyNzM0IDAgOS40MTg4IDAgMjEuMDAwMUMwIDMyLjU4MTUgOS4zODg1MyA0MS45NyAyMC45Njk5IDQxLjk3WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMjAuOTA1MyAzOS42OTU0QzMxLjIxOTIgMzkuNjk1NCAzOS41ODAyIDMxLjMzNDQgMzkuNTgwMiAyMS4wMjA2QzM5LjU4MDIgMTAuNzA2NyAzMS4yMTkyIDIuMzQ1NyAyMC45MDUzIDIuMzQ1N0MxMC41OTE1IDIuMzQ1NyAyLjIzMDQ3IDEwLjcwNjcgMi4yMzA0NyAyMS4wMjA2QzIuMjMwNDcgMzEuMzM0NCAxMC41OTE1IDM5LjY5NTQgMjAuOTA1MyAzOS42OTU0WiIgZmlsbD0iIzAwMzQ1OSIvPgo8cGF0aCBkPSJNMjAuOTA1NCAxMy42NzA5TDMyLjMyNzUgMTQuMzA4OUwyMC45MDU0IDE0Ljk0N0w5LjQ4MzQgMTQuMzA4OUwyMC45MDU0IDEzLjY3MDlaIiBmaWxsPSIjMEE0Qjc3Ii8+CjxwYXRoIGQ9Ik0yMC45MDU0IDI4LjE5NjNMOS40ODMzNCAyNy41NTgyTDIwLjkwNTQgMjYuOTIwMkwzMi4zMjc0IDI3LjU1ODJMMjAuOTA1NCAyOC4xOTYzWiIgZmlsbD0iIzBBNEI3NyIvPgo8cGF0aCBkPSJNMjAuOTA1MyAxMS42MTUyTDMwLjA2MjggMTIuMjUzM0wyMC45MDUzIDEyLjg5MTNMMTEuNzQ3OCAxMi4yNTMzTDIwLjkwNTMgMTEuNjE1MloiIGZpbGw9IiMwQTRCNzciLz4KPHBhdGggZD0iTTIwLjkwNTIgMzAuMjUyTDExLjc0NzggMjkuNjEzOUwyMC45MDUyIDI4Ljk3NTlMMzAuMDYyNyAyOS42MTM5TDIwLjkwNTIgMzAuMjUyWiIgZmlsbD0iIzBBNEI3NyIvPgo8cGF0aCBkPSJNMjAuOTA1MyA5LjU2MDA2TDI3LjQ4NDUgMTAuMTk4MUwyMC45MDUzIDEwLjgzNjFMMTQuMzI2MiAxMC4xOTgxTDIwLjkwNTMgOS41NjAwNloiIGZpbGw9IiMwQTRCNzciLz4KPHBhdGggZD0iTTIwLjkwNTIgMzIuMzA3MUwxNC4zMjYgMzEuNjY5MUwyMC45MDUyIDMxLjAzMUwyNy40ODQ0IDMxLjY2OTFMMjAuOTA1MiAzMi4zMDcxWiIgZmlsbD0iIzBBNEI3NyIvPgo8cGF0aCBkPSJNMzguNzYzOCAyMC43MTQ5QzM4Ljc2MzggMzAuNTc1IDMwLjc2NjIgMzguNTY3OCAyMC45MDYxIDM4LjU2NzhDMTEuMDQ2IDM4LjU2NzggMy4wNTMyMiAzMC41NzUgMy4wNTMyMiAyMC43MTQ5QzMuMDUzMjIgMjAuMDMzMyAzLjA5MTYzIDE5LjM2MTIgMy4xNjg0MyAxOC42OTg3QzQuMTcxNzMgMjcuNjA4NCAxMS43Mjc2IDM0LjUzMDYgMjAuOTA2MSAzNC41MzA2QzMwLjA4NDYgMzQuNTMwNiAzNy42NDUzIDI3LjYwODQgMzguNjQ4NiAxOC42OTg3QzM4LjcyNTQgMTkuMzYxMiAzOC43NjM4IDIwLjAzMzMgMzguNzYzOCAyMC43MTQ5WiIgZmlsbD0iIzAwMkQ0RCIvPgo8cGF0aCBkPSJNMTcuNzQ5NyAyMS4xNzc0TDE0LjY4NzQgMTguNzA2M0MxNC42MDMyIDE4LjYzODYgMTQuNTU0MiAxOC41MzY0IDE0LjU1NDIgMTguNDI4M0wxNC41NTQyIDE2LjUxMjZDMTQuNTU0MiAxNi4zNzM2IDE0LjYzNTIgMTYuMjQ2OSAxNC43NjE0IDE2LjE4ODNMMTcuMzI0NyAxNC45OTk2QzE3LjUxIDE0LjkxMzggMTcuNTg1NiAxNC42OTAxIDE3LjQ5MDggMTQuNTA5MUwxNi42Mzk4IDEyLjg4ODVDMTYuNjEyNiAxMi44MzczIDE2LjU5ODggMTIuNzgwMyAxNi41OTg4IDEyLjcyMjNMMTYuNTk4OCAxMS44NzIzQzE2LjU5ODggMTEuNzQ2MSAxNi42NjU5IDExLjYyODkgMTYuNzc0NSAxMS41NjQ1TDIyLjAxMjkgOC40NzI4OUMyMi4xNDEzIDguMzk3MjcgMjIuMzAzMiA4LjQwODk4IDIyLjQxOTMgOC41MDI3MkwyNy4zODU1IDEyLjUxNDFMMjcuMzg1NSAxNS4yNjUzQzI3LjM4NTUgMTUuNDYyOSAyNy4yMjU3IDE1LjYyMjcgMjcuMDI4MSAxNS42MjI3TDI0LjIwNTYgMTUuNzk4M0MyNC4wMDggMTUuNzk4MyAyMy44NDgyIDE1Ljk1OCAyMy44NDgyIDE2LjE1NTZMMjMuNjE1NCAxOC44MTA3QzIzLjYxNTQgMTkuMDA4MyAyMy43NzUyIDE5LjE2OCAyMy45NzI4IDE5LjE2OEMyNC4xNzA0IDE5LjE2OCAyNC4zMzAxIDE5LjMyNzggMjQuMzMwMSAxOS41MjU0TDI0LjMzMDEgMjEuOTQ1NEMyNC4zMzAxIDIyLjE0MyAyNC4xNzA0IDIyLjMwMjggMjMuOTcyOCAyMi4zMDI4TDIxLjU1MTIgMjIuMzAyOEMyMS41MTY1IDIyLjMwMjggMjEuNDgxOSAyMi4yOTc0IDIxLjQ0ODQgMjIuMjg3OEwxNy43NDkxIDIxLjE3NzQiIGZpbGw9IiMwMEE4RTgiLz4KPHBhdGggZD0iTTI1LjY4NzkgMTkuNDI1NUwyNi40NjggMTguNTQ1OUMyNi41MDM4IDE4LjUwNTQgMjYuNTE5OSAxOC40NTExIDI2LjUxMTkgMTguMzk3NkwyNi4yNzE3IDE2LjgwMDVDMjYuMjYwOSAxNi43Mjg3IDI2LjIwODYgMTYuNjcwMiAyNi4xMzg1IDE2LjY1MTRMMjUuMzU1OSAxNi40NDE3QzI1LjI1ODYgMTYuNDE1NiAyNS4xNTg1IDE2LjQ3MzQgMjUuMTMyNCAxNi41NzA3TDI0LjYxOCAxOC40OTA3QzI0LjU5MTkgMTguNTg4MSAyNC42NDk3IDE4LjY4ODEgMjQuNzQ3IDE4LjcxNDJMMjUuMTY0MiAxOC44MjZDMjUuMjMxIDE4Ljg0MzkgMjUuMjgxOSAxOC44OTggMjUuMjk1OCAxOC45NjU3TDI1LjM3MjYgMTkuMzQxQzI1LjQwMjcgMTkuNDg4MyAyNS41ODgxIDE5LjUzOCAyNS42ODc5IDE5LjQyNTVaIiBmaWxsPSIjMDBBOEU4Ii8+CjxwYXRoIGQ9Ik0xMS42NTA2IDI3LjMwMjdMMTEuNzQ1NCAyNC40MTY2SDEyLjU5OUwxMy4xNDEgMjYuMjUyNkgxMy4xNzA0TDEzLjcxMDEgMjQuNDE2NkgxNC41NjE1TDE0LjY1ODYgMjcuMzAyN0gxNC4wODVMMTQuMDU3OSAyNi4yNjM5TDE0LjAzMzEgMjUuMDY0OEgxMy45OTY5TDEzLjQzMDEgMjYuOTY2MkgxMi44NzkxTDEyLjMxIDI1LjA2NDhIMTIuMjczOEwxMi4yNDkgMjYuMjY2MkwxMi4yMjQyIDI3LjMwMjdIMTEuNjUwNlpNMTYuMzM4NCAyNy4zNjM3QzE1Ljk5NTEgMjcuMzYzNyAxNS43MzQ3IDI3LjI3NTYgMTUuNTU3IDI3LjA5OTVDMTUuMzgwOSAyNi45MjMzIDE1LjI5MjggMjYuNjcyNyAxNS4yOTI4IDI2LjM0NzVWMjYuMDQ3MUMxNS4yOTI4IDI1LjcyMDQgMTUuMzgwOSAyNS40NjkgMTUuNTU3IDI1LjI5MjlDMTUuNzM0NyAyNS4xMTUyIDE1Ljk5NTEgMjUuMDI2NCAxNi4zMzg0IDI1LjAyNjRDMTYuNjgwMSAyNS4wMjY0IDE2LjkzOTEgMjUuMTE1MiAxNy4xMTUyIDI1LjI5MjlDMTcuMjkxNCAyNS40NjkgMTcuMzc5NSAyNS43MjA0IDE3LjM3OTUgMjYuMDQ3MVYyNi4zNDc1QzE3LjM3OTUgMjYuNjcyNyAxNy4yOTE0IDI2LjkyMzMgMTcuMTE1MiAyNy4wOTk1QzE2Ljk0MDYgMjcuMjc1NiAxNi42ODE2IDI3LjM2MzcgMTYuMzM4NCAyNy4zNjM3Wk0xNi4zMzg0IDI2LjkwMDhDMTYuNDg4OSAyNi45MDA4IDE2LjYwMzQgMjYuODU1NiAxNi42ODE2IDI2Ljc2NTNDMTYuNzYxNCAyNi42NzQ5IDE2LjgwMTMgMjYuNTQ1NSAxNi44MDEzIDI2LjM3NjhWMjYuMDE3OEMxNi44MDEzIDI1Ljg0NjEgMTYuNzYxNCAyNS43MTUyIDE2LjY4MTYgMjUuNjI0OEMxNi42MDM0IDI1LjUzMyAxNi40ODg5IDI1LjQ4NzEgMTYuMzM4NCAyNS40ODcxQzE2LjE4NjMgMjUuNDg3MSAxNi4wNzA0IDI1LjUzMyAxNS45OTA2IDI1LjYyNDhDMTUuOTEyMyAyNS43MTUyIDE1Ljg3MzIgMjUuODQ2MSAxNS44NzMyIDI2LjAxNzhWMjYuMzc2OEMxNS44NzMyIDI2LjU0NTUgMTUuOTEyMyAyNi42NzQ5IDE1Ljk5MDYgMjYuNzY1M0MxNi4wNzA0IDI2Ljg1NTYgMTYuMTg2MyAyNi45MDA4IDE2LjMzODQgMjYuOTAwOFpNMTkuNDcxMSAyNy4zMDI3VjI1Ljk3MjZDMTkuNDcxMSAyNS44NzkzIDE5LjQ1ODMgMjUuNzk4NyAxOS40MzI3IDI1LjczMUMxOS40MDg3IDI1LjY2MzIgMTkuMzY4IDI1LjYxMDUgMTkuMzEwOCAyNS41NzI5QzE5LjI1MzYgMjUuNTM1MiAxOS4xNzUzIDI1LjUxNjQgMTkuMDc1OSAyNS41MTY0QzE4Ljk4ODYgMjUuNTE2NCAxOC45MTE4IDI1LjUzMjIgMTguODQ1NiAyNS41NjM4QzE4Ljc4MDggMjUuNTk1NSAxOC43Mjc0IDI1LjYzODQgMTguNjg1MiAyNS42OTI2QzE4LjY0NDYgMjUuNzQ1MyAxOC42MTM3IDI1LjgwNTUgMTguNTkyNyAyNS44NzMyTDE4LjUwMjMgMjUuNTU3MUgxOC42MTA3QzE4LjYzNDggMjUuNDU5MiAxOC42NzQ3IDI1LjM3MTEgMTguNzMwNCAyNS4yOTI5QzE4Ljc4NzYgMjUuMjE0NiAxOC44NjQ0IDI1LjE1MjggMTguOTYwOCAyNS4xMDc3QzE5LjA1ODYgMjUuMDYxIDE5LjE4MDYgMjUuMDM3NyAxOS4zMjY2IDI1LjAzNzdDMTkuNDk2NyAyNS4wMzc3IDE5LjYzNDUgMjUuMDcgMTkuNzM5OSAyNS4xMzQ4QzE5Ljg0NTMgMjUuMTk4IDE5LjkyMjggMjUuMjkyOSAxOS45NzI1IDI1LjQxOTNDMjAuMDIzNyAyNS41NDU4IDIwLjA0OTMgMjUuNzAyNCAyMC4wNDkzIDI1Ljg4OVYyNy4zMDI3SDE5LjQ3MTFaTTE4LjAyODEgMjcuMzAyN1YyNS4wODczSDE4LjYwNjJMMTguNTgzNiAyNS42MjcxTDE4LjYwNjIgMjUuNjc0NVYyNy4zMDI3SDE4LjAyODFaTTIxLjYwODUgMjcuMzUwMkMyMS40MjAzIDI3LjM1MDIgMjEuMjY5OCAyNy4zMjIzIDIxLjE1NjkgMjcuMjY2NkMyMS4wNDU1IDI3LjIwOTQgMjAuOTY0OSAyNy4xMjM2IDIwLjkxNTIgMjcuMDA5MkMyMC44NjU2IDI2Ljg5NDcgMjAuODQwNyAyNi43NTQgMjAuODQwNyAyNi41ODY5VjI1LjI5NTFIMjEuNDE0M1YyNi41MDFDMjEuNDE0MyAyNi42MjE1IDIxLjQ0MTQgMjYuNzEwMyAyMS40OTU2IDI2Ljc2NzVDMjEuNTUxMyAyNi44MjMyIDIxLjY0ODQgMjYuODUxMSAyMS43ODY5IDI2Ljg1MTFDMjEuODY4MiAyNi44NTExIDIxLjk0NjUgMjYuODQyOCAyMi4wMjE4IDI2LjgyNjJDMjIuMDk3MSAyNi44MDgyIDIyLjE2NjMgMjYuNzg0OCAyMi4yMjk2IDI2Ljc1NjJMMjIuMTc5OSAyNy4yMzk1QzIyLjEwNDYgMjcuMjc0MSAyMi4wMTggMjcuMzAxMiAyMS45MjAyIDI3LjMyMDhDMjEuODIzOCAyNy4zNDA0IDIxLjcxOTkgMjcuMzUwMiAyMS42MDg1IDI3LjM1MDJaTTIwLjUyIDI1LjU2MTZWMjUuMTA1NEgyMi4yMDkyTDIyLjE1OTYgMjUuNTYxNkgyMC41MlpNMjAuODQ3NSAyNS4xNDgzTDIwLjg0NTIgMjQuNTYxMkwyMS40MjExIDI0LjUwMjVMMjEuMzk4NSAyNS4xNDgzSDIwLjg0NzVaTTIzLjY5MDYgMjcuMzU5MkMyMy4zNDg5IDI3LjM1OTIgMjMuMDk1MiAyNy4yNjg5IDIyLjkyOTYgMjcuMDg4MkMyMi43NjU1IDI2LjkwNzUgMjIuNjgzNCAyNi42NTM5IDIyLjY4MzQgMjYuMzI3MlYyNi4wNTYyQzIyLjY4MzQgMjUuNzMxIDIyLjc2NjIgMjUuNDc4OCAyMi45MzE4IDI1LjI5OTZDMjMuMDk3NCAyNS4xMjA1IDIzLjM1MDQgMjUuMDMwOSAyMy42OTA2IDI1LjAzMDlDMjMuNzc5NCAyNS4wMzA5IDIzLjg2MjIgMjUuMDM4NCAyMy45MzkgMjUuMDUzNUMyNC4wMTczIDI1LjA2NyAyNC4wODgxIDI1LjA4NTggMjQuMTUxMyAyNS4xMDk5QzI0LjIxNiAyNS4xMzQgMjQuMjcyNSAyNS4xNTk2IDI0LjMyMDcgMjUuMTg2N0wyNC4zNjgxIDI1LjY3MjJDMjQuMjk0MyAyNS42MjU2IDI0LjIxMTUgMjUuNTg2NCAyNC4xMTk3IDI1LjU1NDhDMjQuMDI5NCAyNS41MjMyIDIzLjkyNDcgMjUuNTA3NCAyMy44MDU4IDI1LjUwNzRDMjMuNjE5MSAyNS41MDc0IDIzLjQ4MjEgMjUuNTU1NiAyMy4zOTQ4IDI1LjY1MTlDMjMuMzA5IDI1Ljc0NjggMjMuMjY2MSAyNS44ODUzIDIzLjI2NjEgMjYuMDY3NFYyNi4zMDY4QzIzLjI2NjEgMjYuNDg3NSAyMy4zMTA1IDI2LjYyNjggMjMuMzk5MyAyNi43MjQ2QzIzLjQ4OTYgMjYuODIyNSAyMy42MjgxIDI2Ljg3MTQgMjMuODE0OCAyNi44NzE0QzIzLjkzMzggMjYuODcxNCAyNC4wMzkxIDI2Ljg1NjMgMjQuMTMxIDI2LjgyNjJDMjQuMjIyOCAyNi43OTQ2IDI0LjMwODYgMjYuNzU2MiAyNC4zODg0IDI2LjcxMTFMMjQuMzQxIDI3LjE5ODlDMjQuMjY3MiAyNy4yNDEgMjQuMTc0NiAyNy4yNzc5IDI0LjA2MzIgMjcuMzA5NUMyMy45NTE4IDI3LjM0MjYgMjMuODI3NiAyNy4zNTkyIDIzLjY5MDYgMjcuMzU5MlpNMjQuOTk1NCAyNy4zMDI3VjI0LjMxNzNIMjUuNTc1OFYyNy4zMDI3SDI0Ljk5NTRaTTI2LjMxOTUgMjcuMzAyN1YyNS4wODczSDI2Ljg5NzZWMjcuMzAyN0gyNi4zMTk1Wk0yNi42MDg1IDI0LjgyNTRDMjYuNDk4NiAyNC44MjU0IDI2LjQxNzMgMjQuNzk5OCAyNi4zNjQ2IDI0Ljc0ODZDMjYuMzEzNSAyNC42OTU5IDI2LjI4NzkgMjQuNjIzNiAyNi4yODc5IDI0LjUzMThWMjQuNTIwNUMyNi4yODc5IDI0LjQyODcgMjYuMzEzNSAyNC4zNTY0IDI2LjM2NDYgMjQuMzAzN0MyNi40MTczIDI0LjI1MSAyNi40OTg2IDI0LjIyNDcgMjYuNjA4NSAyNC4yMjQ3QzI2LjcxNjkgMjQuMjI0NyAyNi43OTc1IDI0LjI1MSAyNi44NTAyIDI0LjMwMzdDMjYuOTAyOSAyNC4zNTY0IDI2LjkyOTIgMjQuNDI4NyAyNi45MjkyIDI0LjUyMDVWMjQuNTMxOEMyNi45MjkyIDI0LjYyNTIgMjYuOTAyOSAyNC42OTc0IDI2Ljg1MDIgMjQuNzQ4NkMyNi43OTc1IDI0Ljc5OTggMjYuNzE2OSAyNC44MjU0IDI2LjYwODUgMjQuODI1NFpNMjguNDA5NCAyNC4yNDczQzI4LjUyNjggMjQuMjQ3MyAyOC42MzUyIDI0LjI1NyAyOC43MzQ1IDI0LjI3NjZDMjguODMzOSAyNC4yOTYyIDI4LjkyMiAyNC4zMjAzIDI4Ljk5ODggMjQuMzQ4OUwyOS4wNTA3IDI0Ljc4MjVDMjguOTg2IDI0Ljc2MjkgMjguOTE4MiAyNC43NDcxIDI4Ljg0NzUgMjQuNzM1MUMyOC43NzgyIDI0LjcyMTUgMjguNzAxNCAyNC43MTQ3IDI4LjYxNzEgMjQuNzE0N0MyOC41MTYyIDI0LjcxNDcgMjguNDM2NSAyNC43MjYgMjguMzc3NyAyNC43NDg2QzI4LjMyMDUgMjQuNzcxMiAyOC4yNzk5IDI0LjgwMzYgMjguMjU1OCAyNC44NDU3QzI4LjIzMTcgMjQuODg3OSAyOC4yMTk3IDI0LjkzNzUgMjguMjE5NyAyNC45OTQ4VjI1LjAwMTVDMjguMjE5NyAyNS4wNDIyIDI4LjIyNTcgMjUuMDgwNiAyOC4yMzc3IDI1LjExNjdDMjguMjQ5OCAyNS4xNTI4IDI4LjI2NDEgMjUuMTg1MiAyOC4yODA2IDI1LjIxMzhMMjcuOTAzNSAyNS4yMjc0VjI1LjE2NjRDMjcuODM0MiAyNS4xMzMzIDI3Ljc3NTUgMjUuMDg0MyAyNy43MjczIDI1LjAxOTZDMjcuNjc5MiAyNC45NTQ5IDI3LjY1NTEgMjQuODc0MyAyNy42NTUxIDI0Ljc3OFYyNC43NjY3QzI3LjY1NTEgMjQuNjA3MSAyNy43MTYxIDI0LjQ4MDYgMjcuODM4IDI0LjM4NzNDMjcuOTYxNSAyNC4yOTM5IDI4LjE1MTkgMjQuMjQ3MyAyOC40MDk0IDI0LjI0NzNaTTI3LjczMTkgMjcuMzAyN1YyNS4zNDcxSDI4LjMwNzdWMjcuMzAyN0gyNy43MzE5Wk0yNy40MTEyIDI1LjY0MDZWMjUuMTgyMkwyNy45NzEyIDI1LjE4NjdMMjguMTk3MSAyNS4xODIySDI5LjA0NjJMMjguOTk2NSAyNS42NDA2SDI3LjQxMTJaTTI5Ljg1OTIgMjQuMjQ3M0MyOS45NzY2IDI0LjI0NzMgMzAuMDg1IDI0LjI1NyAzMC4xODQ0IDI0LjI3NjZDMzAuMjgzNyAyNC4yOTYyIDMwLjM3MTggMjQuMzIwMyAzMC40NDg2IDI0LjM0ODlMMzAuNTAwNSAyNC43ODI1QzMwLjQzNTggMjQuNzYyOSAzMC4zNjggMjQuNzQ3MSAzMC4yOTczIDI0LjczNTFDMzAuMjI4IDI0LjcyMTUgMzAuMTUxMiAyNC43MTQ3IDMwLjA2NjkgMjQuNzE0N0MyOS45NjYxIDI0LjcxNDcgMjkuODg2MyAyNC43MjYgMjkuODI3NiAyNC43NDg2QzI5Ljc3MDMgMjQuNzcxMiAyOS43Mjk3IDI0LjgwMzYgMjkuNzA1NiAyNC44NDU3QzI5LjY4MTUgMjQuODg3OSAyOS42Njk1IDI0LjkzNzUgMjkuNjY5NSAyNC45OTQ4VjI1LjAwMTVDMjkuNjY5NSAyNS4wNDIyIDI5LjY3NTUgMjUuMDgwNiAyOS42ODc1IDI1LjExNjdDMjkuNjk5NiAyNS4xNTI4IDI5LjcxMzkgMjUuMTg1MiAyOS43MzA1IDI1LjIxMzhMMjkuMzUzMyAyNS4yMjc0VjI1LjE2NjRDMjkuMjg0MSAyNS4xMzMzIDI5LjIyNTMgMjUuMDg0MyAyOS4xNzcyIDI1LjAxOTZDMjkuMTI5IDI0Ljk1NDkgMjkuMTA0OSAyNC44NzQzIDI5LjEwNDkgMjQuNzc4VjI0Ljc2NjdDMjkuMTA0OSAyNC42MDcxIDI5LjE2NTkgMjQuNDgwNiAyOS4yODc4IDI0LjM4NzNDMjkuNDExMyAyNC4yOTM5IDI5LjYwMTcgMjQuMjQ3MyAyOS44NTkyIDI0LjI0NzNaTTI5LjE4MTcgMjcuMzAyN1YyNS4zNDcxSDI5Ljc1NzZWMjcuMzAyN0gyOS4xODE3Wk0yOC44NjEgMjUuNjQwNlYyNS4xODIyTDI5LjQyMTEgMjUuMTg2N0wyOS42NDY5IDI1LjE4MjJIMzAuNDk2TDMwLjQ0NjMgMjUuNjQwNkgyOC44NjFaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTUuOTg3NCAzMi4yODUyVjMxLjU1MDhIMTYuNzY5NEMxNi45OTM2IDMxLjU1MDggMTcuMTYxNyAzMS40OTAxIDE3LjI3MzkgMzEuMzY4N0MxNy4zODYgMzEuMjQ3MiAxNy40NDIgMzEuMDczNCAxNy40NDIgMzAuODQ3M1YzMC4xMjk4QzE3LjQ0MiAyOS45MDM3IDE3LjM4NiAyOS43MzA5IDE3LjI3MzkgMjkuNjExM0MxNy4xNjE3IDI5LjQ4OTkgMTYuOTkzNiAyOS40MjkxIDE2Ljc2OTQgMjkuNDI5MUgxNS45ODQ2VjI4LjcwMzJIMTYuODAwMkMxNy4zMTAzIDI4LjcwMzIgMTcuNjkxNSAyOC44MjY1IDE3Ljk0MzcgMjkuMDczMkMxOC4xOTc4IDI5LjMxOCAxOC4zMjQ5IDI5LjY3MDIgMTguMzI0OSAzMC4xMjk4VjMwLjg1MDFDMTguMzI0OSAzMS4zMTE3IDE4LjE5ODggMzEuNjY2NyAxNy45NDY1IDMxLjkxNTJDMTcuNjk0MyAzMi4xNjE4IDE3LjMxMjIgMzIuMjg1MiAxNi44MDAyIDMyLjI4NTJIMTUuOTg3NFpNMTUuMzczNiAzMi4yODUyVjI4LjcwMzJIMTYuMjM2OFYzMi4yODUySDE1LjM3MzZaTTE5LjA4NTIgMzIuMjg1MkwxOS4xOTczIDI4LjcwMzJIMjAuNDE5M0wyMS4wMTA3IDMwLjgzODlIMjEuMDQ3MkwyMS42Mzg1IDI4LjcwMzJIMjIuODYwNkwyMi45NzI3IDMyLjI4NTJIMjIuMTIzNEwyMi4wOTgyIDMxLjExMDhMMjIuMDczIDI5LjY4MTRIMjIuMDMwOUwyMS40MTcxIDMxLjg2NDdIMjAuNjM4TDIwLjAyNDIgMjkuNjgxNEgxOS45NzkzTDE5Ljk1NjkgMzEuMTEzNkwxOS45MzQ1IDMyLjI4NTJIMTkuMDg1MlpNMjQuNTIwOCAzMi4yODUyTDIzLjUzOTggMjguNzAzMkgyNC40NDc5TDI1LjE1MTQgMzEuNjMyMUgyNS4yMTU5TDI1LjkyMjIgMjguNzAzMkgyNi44Mjc1TDI1Ljg0OTMgMzIuMjg1MkgyNC41MjA4WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMjAuOTc0MSAzLjU5OTYxTDIwLjU2NTggNC40MjQ4OUwxOS42NTQ1IDQuNTU4MTRMMjAuMzEyMiA1LjIwMjg5TDIwLjE1NzQgNi4xMTQxM0wyMC45NzQxIDUuNjg0M0wyMS43OTA4IDYuMTE0MTNMMjEuNjM2MSA1LjIwMjg5TDIyLjI5MzcgNC41NTgxNEwyMS4zODI1IDQuNDI0ODlMMjAuOTc0MSAzLjU5OTYxWiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMjYuOTI3MyA0LjY3NDMyTDI2LjI2MSA1LjMxMDQ3TDI1LjM1ODQgNS4xMjEzNEwyNS43NTgxIDUuOTU1MjJMMjUuMzAyNSA2Ljc1NDcxTDI2LjIxMzcgNi42MzAwNkwyNi44MzcgNy4zMTM0OUwyNy4wMDAzIDYuNDA2NTRMMjcuODQyOCA2LjAyODI5TDI3LjAzMDQgNS41ODk4NkwyNi45MjczIDQuNjc0MzJaIiBmaWxsPSIjREM4NTQ4Ii8+CjxwYXRoIGQ9Ik0zMi4xNTgzIDcuNzE3MjNMMzEuMzExNSA4LjA5MTE4TDMwLjUyOTIgNy42MDU0N0wzMC42MTk1IDguNTIxMDFMMjkuOTE0NiA5LjExODQ4TDMwLjgxNzIgOS4zMTYyTDMxLjE2NTQgMTAuMTY3M0wzMS42Mjk2IDkuMzcyMDhMMzIuNTQ5NCA5LjMwMzMxTDMxLjkzOTEgOC42MTU1OEwzMi4xNTgzIDcuNzE3MjNaIiBmaWxsPSIjREM4NTQ4Ii8+CjxwYXRoIGQ9Ik0zNi4wMjY5IDEyLjM2ODVMMzUuMTA3MSAxMi40Mjg3TDM0LjUzNTQgMTEuNzA2NUwzNC4zMDc2IDEyLjYwMDZMMzMuNDQzNiAxMi45MTg3TDM0LjIyMTYgMTMuNDA4N0wzNC4yNjAzIDE0LjMzMjhMMzQuOTY5NSAxMy43NDRMMzUuODU1IDEzLjk5MzNMMzUuNTE1NCAxMy4xMzc5TDM2LjAyNjkgMTIuMzY4NVoiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTM4LjA3MjkgMTguMDYzNUwzNy4xOTE3IDE3LjgwNTZMMzYuODk5NCAxNi45Mjg3TDM2LjM3OTMgMTcuNjg5NUwzNS40NTk1IDE3LjY5MzhMMzYuMDIyNiAxOC40MjQ1TDM1Ljc0MzIgMTkuMzAxNEwzNi42MTE0IDE4Ljk5MTlMMzcuMzU5MyAxOS41MjkyTDM3LjMzMzUgMTguNjA5NEwzOC4wNzI5IDE4LjA2MzVaIiBmaWxsPSIjREM4NTQ4Ii8+CjxwYXRoIGQ9Ik0zOC4wNTEzIDI0LjExMDdMMzcuMzA3NyAyMy41NjQ5TDM3LjMzNzggMjIuNjQ1TDM2LjU4NTUgMjMuMTgyM0wzNS43MTczIDIyLjg3MjhMMzYuMDAxIDIzLjc0OTdMMzUuNDMzNiAyNC40ODA0TDM2LjM1NzcgMjQuNDg0N0wzNi44Nzc4IDI1LjI0NTVMMzcuMTY1OCAyNC4zNjg2TDM4LjA1MTMgMjQuMTEwN1oiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTM1Ljk1ODEgMjkuNzg5NUwzNS40NDY1IDI5LjAyNDRMMzUuNzkwNCAyOC4xNjQ4TDM0LjkwMDcgMjguNDE4NEwzNC4xOTE0IDI3LjgyNTJMMzQuMTU3MSAyOC43NDkzTDMzLjM3NDggMjkuMjM5M0wzNC4yNDMgMjkuNTYxN0wzNC40NjY1IDMwLjQ1NThMMzUuMDM4MiAyOS43Mjk0TDM1Ljk1ODEgMjkuNzg5NVoiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTMyLjA1NTIgMzQuNDFMMzEuODM2IDMzLjUxNkwzMi40NDY0IDMyLjgyMzlMMzEuNTI2NiAzMi43NTUyTDMxLjA2MjMgMzEuOTZMMzAuNzE0MiAzMi44MTUzTDI5LjgxMTUgMzMuMDA4OEwzMC41MTY0IDMzLjYwNjJMMzAuNDI2MiAzNC41MjYxTDMxLjIwODUgMzQuMDQwNEwzMi4wNTUyIDM0LjQxWiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMjYuODAyNyAzNy40MTQ2TDI2LjkwMTUgMzYuNDk5TDI3LjcxMzkgMzYuMDYwNkwyNi44NzU3IDM1LjY4MjNMMjYuNzA4MSAzNC43NzU0TDI2LjA4OTEgMzUuNDU4OEwyNS4xNzM2IDM1LjMzNDJMMjUuNjMzNSAzNi4xMzM3TDI1LjIzMzggMzYuOTYzMkwyNi4xMzY0IDM2Ljc3ODRMMjYuODAyNyAzNy40MTQ2WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMjAuODQwNyAzOC40NDE5TDIxLjI0OTEgMzcuNjE2NkwyMi4xNjAzIDM3LjQ4MzNMMjEuNDk4NCAzNi44Mzg2TDIxLjY1NzQgMzUuOTMxNkwyMC44NDA3IDM2LjM2MTVMMjAuMDI0IDM1LjkzMTZMMjAuMTc4OCAzNi44Mzg2TDE5LjUxNjggMzcuNDgzM0wyMC40MzI0IDM3LjYxNjZMMjAuODQwNyAzOC40NDE5WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMTQuODgzNCAzNy4zNjcyTDE1LjU0OTcgMzYuNzMxMUwxNi40NTIzIDM2LjkyMDJMMTYuMDUyNiAzNi4wODYzTDE2LjUxMjUgMzUuMjg2OEwxNS41OTY5IDM1LjQxMTVMMTQuOTc4IDM0LjcyOEwxNC44MTAzIDM1LjYzNUwxMy45NzIyIDM2LjAxNzVMMTQuNzg0NiAzNi40NTE3TDE0Ljg4MzQgMzcuMzY3MloiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTkuNjU2NjggMzQuMzI0MUwxMC40OTkxIDMzLjk1NDRMMTEuMjg1NyAzNC40MzU4TDExLjE5MTIgMzMuNTIwM0wxMS44OTYxIDMyLjkyMjhMMTAuOTkzNSAzMi43Mjk0TDEwLjY0NTMgMzEuODc0TDEwLjE4MTEgMzIuNjY5Mkw5LjI2MTIzIDMyLjczOEw5Ljg3NTg5IDMzLjQyNTdMOS42NTY2OCAzNC4zMjQxWiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNNS43ODM2OSAyOS42NzMzTDYuNzAzNTMgMjkuNjEzMUw3LjI3NTIxIDMwLjMzOTZMNy41MDMwMiAyOS40NDU1TDguMzY2OTkgMjkuMTIzMUw3LjU4ODk5IDI4LjYzMzFMNy41NTQ2IDI3LjcwOUw2Ljg0NTM4IDI4LjI5NzlMNS45NTU2MiAyOC4wNDg2TDYuMjk1MTkgMjguOTA4Mkw1Ljc4MzY5IDI5LjY3MzNaIiBmaWxsPSIjREM4NTQ4Ii8+CjxwYXRoIGQ9Ik0zLjczNzc5IDIzLjk3NzlMNC42MjMyNSAyNC4yNDAxTDQuOTExMjQgMjUuMTEyN0w1LjQzMTMzIDI0LjM1MTlMNi4zNTU0NyAyNC4zNDc2TDUuNzg4MSAyMy42MTY5TDYuMDcxNzkgMjIuNzRMNS4yMDM1MiAyMy4wNDk1TDQuNDUxMzIgMjIuNTEyMkw0LjQ4MTQgMjMuNDM2M0wzLjczNzc5IDIzLjk3NzlaIiBmaWxsPSIjREM4NTQ4Ii8+CjxwYXRoIGQ9Ik0zLjc1OTI4IDE3LjkzMDJMNC41MDI4OSAxOC40NzYxTDQuNDc3MSAxOS4zOTU5TDUuMjI1MDEgMTguODU4Nkw2LjA5MzI3IDE5LjE2ODFMNS44MTM4OCAxOC4yOTEyTDYuMzc2OTYgMTcuNTYwNUw1LjQ1MjgyIDE3LjU1NjJMNC45MzcwMiAxNi43OTU0TDQuNjQ0NzMgMTcuNjcyM0wzLjc1OTI4IDE3LjkzMDJaIiBmaWxsPSIjREM4NTQ4Ii8+CjxwYXRoIGQ9Ik01Ljg1MjU0IDEyLjI1MjJMNi4zNjQwNCAxMy4wMTczTDYuMDI0NDcgMTMuODc2OUw2LjkwOTkzIDEzLjYyNzZMNy42MTkxNSAxNC4yMTY1TDcuNjU3ODQgMTMuMjkyNEw4LjQzNTgzIDEyLjgwMjRMNy41NzE4NyAxMi40OEw3LjM0NDA2IDExLjU4NTlMNi43NzIzOCAxMi4zMTI0TDUuODUyNTQgMTIuMjUyMloiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTkuNzU5NyA3LjYzMTc4TDkuOTc4OTIgOC41MzAxM0w5LjM2NDI2IDkuMjE3ODZMMTAuMjg0MSA5LjI4NjYzTDEwLjc0ODMgMTAuMDgxOEwxMS4xMDA4IDkuMjI2NDZMMTEuOTk5MSA5LjAzMzAzTDExLjI5NDIgOC40MzU1NkwxMS4zODg4IDcuNTIwMDJMMTAuNjAyMiA4LjAwMTQzTDkuNzU5NyA3LjYzMTc4WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMTUuMDEyNSA0LjYyNjk1TDE0LjkwOTMgNS41NDI1TDE0LjA5NjkgNS45ODA5M0wxNC45Mzk0IDYuMzU5MThMMTUuMTAyNyA3LjI2NjEzTDE1LjcyNiA2LjU4MjY5TDE2LjYzNzIgNi43MDczNUwxNi4xODE2IDUuOTA3ODZMMTYuNTgxNCA1LjA3ODI4TDE1LjY3ODcgNS4yNjMxMUwxNS4wMTI1IDQuNjI2OTVaIiBmaWxsPSIjREM4NTQ4Ii8+CjxsaW5lIHgxPSI0OC45NzM4IiB5MT0iNC4zMjE3OCIgeDI9IjQ4Ljk3MzgiIHkyPSIzNy42Nzg0IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjAuNjExNjUzIi8+CjxwYXRoIGQ9Ik01Ny40MTEgMTguODkwNlYxNy40NjA2SDU5LjI5NkM1OS44ODk3IDE3LjQ2MDYgNjAuMzMzOCAxNy4zMDAzIDYwLjYyODUgMTYuOTc5NkM2MC45Mjc1IDE2LjY1NDYgNjEuMDc3IDE2LjE5MSA2MS4wNzcgMTUuNTg4NlYxMy44NjYxQzYxLjA3NyAxMy4yNjM4IDYwLjkyNzUgMTIuODAyMyA2MC42Mjg1IDEyLjQ4MTZDNjAuMzMzOCAxMi4xNjEgNTkuODg5NyAxMi4wMDA2IDU5LjI5NiAxMi4wMDA2SDU3LjQwNDVWMTAuNTgzNkg1OS4zNTQ1QzYwLjQ5ODUgMTAuNTgzNiA2MS4zNTg3IDEwLjg2NTMgNjEuOTM1IDExLjQyODZDNjIuNTExMyAxMS45OTIgNjIuNzk5NSAxMi44MDIzIDYyLjc5OTUgMTMuODU5NlYxNS41OTUxQzYyLjc5OTUgMTYuNjU2OCA2Mi41MTEzIDE3LjQ3MTUgNjEuOTM1IDE4LjAzOTFDNjEuMzYzIDE4LjYwNjggNjAuNTAyOCAxOC44OTA2IDU5LjM1NDUgMTguODkwNkg1Ny40MTFaTTU2LjIyMTUgMTguODkwNlYxMC41ODM2SDU3LjkwNVYxOC44OTA2SDU2LjIyMTVaTTY3LjA2NzggMTkuMDQ2NkM2NS45NzU4IDE5LjA0NjYgNjUuMTYzMyAxOC43OTUzIDY0LjYzMDMgMTguMjkyNkM2NC4wOTczIDE3Ljc5IDYzLjgzMDggMTcuMDYyIDYzLjgzMDggMTYuMTA4NlYxNS4yNzY2QzYzLjgzMDggMTQuMzMyIDY0LjA3OTkgMTMuNjA2MSA2NC41NzgzIDEzLjA5OTFDNjUuMDc2NiAxMi41OTIxIDY1LjgwMDMgMTIuMzM4NiA2Ni43NDkzIDEyLjMzODZDNjcuMzkwNiAxMi4zMzg2IDY3LjkyNTggMTIuNDUxMyA2OC4zNTQ4IDEyLjY3NjZDNjguNzgzOCAxMi45MDIgNjkuMTA0NCAxMy4yMjI2IDY5LjMxNjggMTMuNjM4NkM2OS41MzM0IDE0LjA1MDMgNjkuNjQxOCAxNC41NDQzIDY5LjY0MTggMTUuMTIwNlYxNS4zNDgxQzY5LjY0MTggMTUuNTA0MSA2OS42MzMxIDE1LjY2NDUgNjkuNjE1OCAxNS44MjkxQzY5LjYwMjggMTUuOTg5NSA2OS41ODMzIDE2LjE0MTEgNjkuNTU3MyAxNi4yODQxSDY4LjA0OTNDNjguMDYyMyAxNi4wNDU4IDY4LjA2ODggMTUuODIwNSA2OC4wNjg4IDE1LjYwODFDNjguMDczMSAxNS4zOTE1IDY4LjA3NTMgMTUuMTk2NSA2OC4wNzUzIDE1LjAyMzFDNjguMDc1MyAxNC43MjQxIDY4LjAyNzYgMTQuNDcwNiA2Ny45MzIzIDE0LjI2MjZDNjcuODM2OSAxNC4wNTAzIDY3LjY5MTggMTMuODkgNjcuNDk2OCAxMy43ODE2QzY3LjMwMTggMTMuNjczMyA2Ny4wNTI2IDEzLjYxOTEgNjYuNzQ5MyAxMy42MTkxQzY2LjMwMjkgMTMuNjE5MSA2NS45NzM2IDEzLjc0MjYgNjUuNzYxMyAxMy45ODk2QzY1LjU0ODkgMTQuMjM2NiA2NS40NDI4IDE0LjU4NzYgNjUuNDQyOCAxNS4wNDI2VjE1LjYzNDFMNjUuNDQ5MyAxNS44MjI2VjE2LjMyMzFDNjUuNDQ5MyAxNi41MjI1IDY1LjQ3OTYgMTYuNzA2NiA2NS41NDAzIDE2Ljg3NTZDNjUuNjA1MyAxNy4wNDQ2IDY1LjcxMTQgMTcuMTkyIDY1Ljg1ODggMTcuMzE3NkM2Ni4wMDYxIDE3LjQzOSA2Ni4yMDExIDE3LjUzNDMgNjYuNDQzOCAxNy42MDM2QzY2LjY5MDggMTcuNjczIDY2Ljk5ODQgMTcuNzA3NiA2Ny4zNjY4IDE3LjcwNzZDNjcuNzY1NCAxNy43MDc2IDY4LjE0NDYgMTcuNjY0MyA2OC41MDQzIDE3LjU3NzZDNjguODY4MyAxNy40ODY2IDY5LjIxMjggMTcuMzY1MyA2OS41Mzc4IDE3LjIxMzZMNjkuMzk0OCAxOC41MjY2QzY5LjEwNDQgMTguNjg3IDY4Ljc2MjEgMTguODEyNiA2OC4zNjc4IDE4LjkwMzZDNjcuOTc3OCAxOC45OTkgNjcuNTQ0NCAxOS4wNDY2IDY3LjA2NzggMTkuMDQ2NlpNNjQuNzE0OCAxNi4yODQxVjE1LjE3OTFINjkuMjE5M1YxNi4yODQxSDY0LjcxNDhaTTc0LjQ1MjUgMTkuMDQwMUM3NC4wNzExIDE5LjA0MDEgNzMuNzQ4MyAxOC45Nzk1IDczLjQ4NCAxOC44NTgxQzczLjIxOTYgMTguNzMyNSA3My4wMDUxIDE4LjU1NyA3Mi44NDA1IDE4LjMzMTZDNzIuNjgwMSAxOC4xMDYzIDcyLjU2NTMgMTcuODQyIDcyLjQ5NiAxNy41Mzg2SDcyLjAyMTVMNzIuNDQ0IDE2LjI2NDZDNzIuNDUyNiAxNi41NjggNzIuNTEzMyAxNi44MjM2IDcyLjYyNiAxNy4wMzE2QzcyLjc0MyAxNy4yMzk2IDcyLjkwMzMgMTcuMzk1NiA3My4xMDcgMTcuNDk5NkM3My4zMTUgMTcuNjAzNiA3My41NTc2IDE3LjY1NTYgNzMuODM1IDE3LjY1NTZDNzQuMjU1MyAxNy42NTU2IDc0LjU3NiAxNy41MzQzIDc0Ljc5NyAxNy4yOTE2Qzc1LjAxOCAxNy4wNDQ2IDc1LjEyODUgMTYuNjgwNiA3NS4xMjg1IDE2LjE5OTZWMTUuMTUzMUM3NS4xMjg1IDE0LjY3NjUgNzUuMDIwMSAxNC4zMTY4IDc0LjgwMzUgMTQuMDc0MUM3NC41ODY4IDEzLjgzMTUgNzQuMjY2MSAxMy43MTAxIDczLjg0MTUgMTMuNzEwMUM3My41OTg4IDEzLjcxMDEgNzMuMzggMTMuNzU3OCA3My4xODUgMTMuODUzMUM3Mi45OSAxMy45NDQxIDcyLjgyNzUgMTQuMDY3NiA3Mi42OTc1IDE0LjIyMzZDNzIuNTY3NSAxNC4zNzk2IDcyLjQ3NDMgMTQuNTU5NSA3Mi40MTggMTQuNzYzMUw3Mi4wMjggMTMuODY2MUg3Mi40ODk1QzcyLjU1ODggMTMuNTg4OCA3Mi42NjkzIDEzLjMzNzUgNzIuODIxIDEzLjExMjFDNzIuOTc3IDEyLjg4MjUgNzMuMTkxNSAxMi43MDI2IDczLjQ2NDUgMTIuNTcyNkM3My43NDE4IDEyLjQzODMgNzQuMDkwNiAxMi4zNzExIDc0LjUxMSAxMi4zNzExQzc1LjI2MDYgMTIuMzcxMSA3NS44MzA1IDEyLjYxMzggNzYuMjIwNSAxMy4wOTkxQzc2LjYxMDUgMTMuNTgwMSA3Ni44MDU1IDE0LjI5MyA3Ni44MDU1IDE1LjIzNzZWMTYuMTIxNkM3Ni44MDU1IDE3LjA3NSA3Ni42MDgzIDE3LjgwMDggNzYuMjE0IDE4LjI5OTFDNzUuODI0IDE4Ljc5MzEgNzUuMjM2OCAxOS4wNDAxIDc0LjQ1MjUgMTkuMDQwMVpNNzAuODEyNSAyMS4xMjY2VjEyLjUxNDFINzIuNDc2NUw3Mi40MTE1IDE0LjEzMjZMNzIuNDQ0IDE0LjQyNTFWMTYuOTc5Nkw3Mi40MjQ1IDE3LjI3ODZMNzIuNDcgMTkuMDI3MVYyMS4xMjY2SDcwLjgxMjVaTTgxLjcwMjcgMTguODkwNkw4MS43NjEyIDE3LjMzMDZMODEuNzE1NyAxNy4xODc2VjE1LjE5MjFMODEuNzA5MiAxNC45MDYxQzgxLjcwOTIgMTQuNDkwMSA4MS41OTQ0IDE0LjE4NDYgODEuMzY0NyAxMy45ODk2QzgxLjEzOTQgMTMuNzk0NiA4MC43Njg5IDEzLjY5NzEgODAuMjUzMiAxMy42OTcxQzc5LjgxNTUgMTMuNjk3MSA3OS40MDM5IDEzLjc1NTYgNzkuMDE4MiAxMy44NzI2Qzc4LjYzNjkgMTMuOTg1MyA3OC4yODM3IDE0LjExNzUgNzcuOTU4NyAxNC4yNjkxTDc4LjEwMTcgMTIuOTQzMUM3OC4yOTI0IDEyLjg0MzUgNzguNTA5IDEyLjc1MDMgNzguNzUxNyAxMi42NjM2Qzc4Ljk5ODcgMTIuNTcyNiA3OS4yNzM5IDEyLjQ5OSA3OS41NzcyIDEyLjQ0MjZDNzkuODgwNSAxMi4zODYzIDgwLjIwNzcgMTIuMzU4MSA4MC41NTg3IDEyLjM1ODFDODEuMDc4NyAxMi4zNTgxIDgxLjUxODUgMTIuNDIxIDgxLjg3ODIgMTIuNTQ2NkM4Mi4yMzc5IDEyLjY2OCA4Mi41MjM5IDEyLjg0MzUgODIuNzM2MiAxMy4wNzMxQzgyLjk1MjkgMTMuMzAyOCA4My4xMDg5IDEzLjU3OCA4My4yMDQyIDEzLjg5ODZDODMuMjk5NSAxNC4yMTUgODMuMzQ3MiAxNC41NjYgODMuMzQ3MiAxNC45NTE2VjE4Ljg5MDZIODEuNzAyN1pNNzkuNjE2MiAxOS4wNDAxQzc4Ljk4MzUgMTkuMDQwMSA3OC41MDA0IDE4Ljg4MiA3OC4xNjY3IDE4LjU2NTZDNzcuODM3NCAxOC4yNDkzIDc3LjY3MjcgMTcuNzk4NiA3Ny42NzI3IDE3LjIxMzZWMTcuMDMxNkM3Ny42NzI3IDE2LjQxMiA3Ny44NjM0IDE1Ljk1NDggNzguMjQ0NyAxNS42NjAxQzc4LjYyNiAxNS4zNjExIDc5LjIzMDUgMTUuMTU1MyA4MC4wNTgyIDE1LjA0MjZMODEuODY1MiAxNC43OTU2TDgxLjk2MjcgMTUuODY4MUw4MC4yOTg3IDE2LjEwODZDNzkuOTM0NyAxNi4xNTYzIDc5LjY3NDcgMTYuMjQzIDc5LjUxODcgMTYuMzY4NkM3OS4zNjcgMTYuNDk0MyA3OS4yOTEyIDE2LjY3ODUgNzkuMjkxMiAxNi45MjExVjE2Ljk4NjFDNzkuMjkxMiAxNy4yMjQ1IDc5LjM2NDkgMTcuNDEwOCA3OS41MTIyIDE3LjU0NTFDNzkuNjYzOSAxNy42NzUxIDc5LjkgMTcuNzQwMSA4MC4yMjA3IDE3Ljc0MDFDODAuNTA2NyAxNy43NDAxIDgwLjc1MTUgMTcuNjk0NiA4MC45NTUyIDE3LjYwMzZDODEuMTU4OSAxNy41MTI2IDgxLjMyNTcgMTcuMzkzNSA4MS40NTU3IDE3LjI0NjFDODEuNTkgMTcuMDk0NSA4MS42ODU0IDE2LjkyNTUgODEuNzQxNyAxNi43MzkxTDgxLjk3NTcgMTcuNTY0Nkg4MS42ODk3QzgxLjYyMDQgMTcuODM3NiA4MS41MDc3IDE4LjA4NjggODEuMzUxNyAxOC4zMTIxQzgxLjIgMTguNTMzMSA4MC45ODU1IDE4LjcxMDggODAuNzA4MiAxOC44NDUxQzgwLjQzMDkgMTguOTc1MSA4MC4wNjY5IDE5LjA0MDEgNzkuNjE2MiAxOS4wNDAxWk04Ni4zMzIzIDE1LjE4NTZMODUuOTA5OCAxNC4wNzQxSDg2LjMxMjhDODYuNDI5OCAxMy41NTg1IDg2LjY0MjIgMTMuMTUzMyA4Ni45NDk4IDEyLjg1ODZDODcuMjU3NSAxMi41NjQgODcuNjg0MyAxMi40MTY2IDg4LjIzMDMgMTIuNDE2NkM4OC4zNDMgMTIuNDE2NiA4OC40NDQ4IDEyLjQyNTMgODguNTM1OCAxMi40NDI2Qzg4LjYyNjggMTIuNDU1NiA4OC43MDkyIDEyLjQ3MyA4OC43ODI4IDEyLjQ5NDZMODguODczOCAxNC4xNTg2Qzg4Ljc3ODUgMTQuMTI4MyA4OC42NjggMTQuMTA2NiA4OC41NDIzIDE0LjA5MzZDODguNDE2NyAxNC4wNzYzIDg4LjI4NDUgMTQuMDY3NiA4OC4xNDU4IDE0LjA2NzZDODcuNzAzOCAxNC4wNjc2IDg3LjMyNjggMTQuMTY1MSA4Ny4wMTQ4IDE0LjM2MDFDODYuNzA3MiAxNC41NTUxIDg2LjQ3OTcgMTQuODMwMyA4Ni4zMzIzIDE1LjE4NTZaTTg0LjcxMzggMTguODkwNlYxMi41MTQxSDg2LjI5OThMODYuMjI4MyAxNC40NzA2TDg2LjM3NzggMTQuNTI5MVYxOC44OTA2SDg0LjcxMzhaTTkyLjQ3NzggMTkuMDI3MUM5MS45MzYxIDE5LjAyNzEgOTEuNTAyOCAxOC45NDcgOTEuMTc3OCAxOC43ODY2QzkwLjg1NzEgMTguNjIyIDkwLjYyNTMgMTguMzc1IDkwLjQ4MjMgMTguMDQ1NkM5MC4zMzkzIDE3LjcxNjMgOTAuMjY3OCAxNy4zMTExIDkwLjI2NzggMTYuODMwMVYxMy4xMTIxSDkxLjkxODhWMTYuNTgzMUM5MS45MTg4IDE2LjkyOTggOTEuOTk2OCAxNy4xODU1IDkyLjE1MjggMTcuMzUwMUM5Mi4zMTMxIDE3LjUxMDUgOTIuNTkyNiAxNy41OTA2IDkyLjk5MTMgMTcuNTkwNkM5My4yMjUzIDE3LjU5MDYgOTMuNDUwNiAxNy41NjY4IDkzLjY2NzMgMTcuNTE5MUM5My44ODQgMTcuNDY3MSA5NC4wODMzIDE3LjQgOTQuMjY1MyAxNy4zMTc2TDk0LjEyMjMgMTguNzA4NkM5My45MDU2IDE4LjgwODMgOTMuNjU2NSAxOC44ODYzIDkzLjM3NDggMTguOTQyNkM5My4wOTc1IDE4Ljk5OSA5Mi43OTg1IDE5LjAyNzEgOTIuNDc3OCAxOS4wMjcxWk04OS4zNDQ4IDEzLjg3OTFWMTIuNTY2MUg5NC4yMDY4TDk0LjA2MzggMTMuODc5MUg4OS4zNDQ4Wk05MC4yODczIDEyLjY4OTZMOTAuMjgwOCAxMC45OTk2TDkxLjkzODMgMTAuODMwNkw5MS44NzMzIDEyLjY4OTZIOTAuMjg3M1pNMTAzLjA4NCAxOC44OTA2VjE1LjAxNjZDMTAzLjA4NCAxNC43NTY2IDEwMy4wNTIgMTQuNTMzNSAxMDIuOTg3IDE0LjM0NzFDMTAyLjkyMiAxNC4xNTY1IDEwMi44MTMgMTQuMDA5MSAxMDIuNjYyIDEzLjkwNTFDMTAyLjUxNCAxMy44MDExIDEwMi4zMTMgMTMuNzQ5MSAxMDIuMDU3IDEzLjc0OTFDMTAxLjgyMyAxMy43NDkxIDEwMS42MTkgMTMuNzk0NiAxMDEuNDQ2IDEzLjg4NTZDMTAxLjI3MyAxMy45NzY2IDEwMS4xMzIgMTQuMTAwMSAxMDEuMDI0IDE0LjI1NjFDMTAwLjkxNSAxNC40MDc4IDEwMC44MzUgMTQuNTgxMSAxMDAuNzgzIDE0Ljc3NjFMMTAwLjYwMSAxMy44NjYxSDEwMC43N0MxMDAuODM5IDEzLjU5MzEgMTAwLjk1MiAxMy4zNDQgMTAxLjEwOCAxMy4xMTg2QzEwMS4yNjQgMTIuODg5IDEwMS40NzYgMTIuNzA3IDEwMS43NDUgMTIuNTcyNkMxMDIuMDE4IDEyLjQzODMgMTAyLjM2MyAxMi4zNzExIDEwMi43NzkgMTIuMzcxMUMxMDMuMjQyIDEyLjM3MTEgMTAzLjYxNyAxMi40NjIxIDEwMy45MDMgMTIuNjQ0MUMxMDQuMTkzIDEyLjgyMTggMTA0LjQwOCAxMy4wOTA1IDEwNC41NDcgMTMuNDUwMUMxMDQuNjg1IDEzLjgwNTUgMTA0Ljc1NSAxNC4yNDc1IDEwNC43NTUgMTQuNzc2MVYxOC44OTA2SDEwMy4wODRaTTk1LjIyNTYgMTguODkwNlYxMi41MTQxSDk2Ljg4OTZMOTYuODI0NiAxNC4xMzI2TDk2Ljg4OTYgMTQuMjA0MVYxOC44OTA2SDk1LjIyNTZaTTk5LjE1ODEgMTguODkwNlYxNS4wMTY2Qzk5LjE1ODEgMTQuNzU2NiA5OS4xMjU2IDE0LjUzMzUgOTkuMDYwNiAxNC4zNDcxQzk4Ljk5NTYgMTQuMTU2NSA5OC44ODcyIDE0LjAwOTEgOTguNzM1NiAxMy45MDUxQzk4LjU4ODIgMTMuODAxMSA5OC4zODY3IDEzLjc0OTEgOTguMTMxMSAxMy43NDkxQzk3Ljg5MjcgMTMuNzQ5MSA5Ny42ODY5IDEzLjc5NDYgOTcuNTEzNiAxMy44ODU2Qzk3LjM0NDYgMTMuOTc2NiA5Ny4yMDM3IDE0LjEwMDEgOTcuMDkxMSAxNC4yNTYxQzk2Ljk4MjcgMTQuNDA3OCA5Ni45MDI2IDE0LjU4MTEgOTYuODUwNiAxNC43NzYxTDk2LjU5MDYgMTMuODY2MUg5Ni45MDI2Qzk2Ljk2NzYgMTMuNTg0NSA5Ny4wNzU5IDEzLjMzMSA5Ny4yMjc2IDEzLjEwNTZDOTcuMzgzNiAxMi44ODAzIDk3LjU5MzcgMTIuNzAyNiA5Ny44NTgxIDEyLjU3MjZDOTguMTIyNCAxMi40MzgzIDk4LjQ1MTcgMTIuMzcxMSA5OC44NDYxIDEyLjM3MTFDOTkuNDM5NyAxMi4zNzExIDk5Ljg4NjEgMTIuNTIyOCAxMDAuMTg1IDEyLjgyNjFDMTAwLjQ4OCAxMy4xMjk1IDEwMC42NzkgMTMuNTcxNSAxMDAuNzU3IDE0LjE1MjFDMTAwLjc3NCAxNC4yMzg4IDEwMC43OSAxNC4zNDA2IDEwMC44MDMgMTQuNDU3NkMxMDAuODE2IDE0LjU3MDMgMTAwLjgyMiAxNC42NzY1IDEwMC44MjIgMTQuNzc2MVYxOC44OTA2SDk5LjE1ODFaTTEwOS4xNCAxOS4wNDY2QzEwOC4wNDggMTkuMDQ2NiAxMDcuMjM2IDE4Ljc5NTMgMTA2LjcwMyAxOC4yOTI2QzEwNi4xNyAxNy43OSAxMDUuOTAzIDE3LjA2MiAxMDUuOTAzIDE2LjEwODZWMTUuMjc2NkMxMDUuOTAzIDE0LjMzMiAxMDYuMTUyIDEzLjYwNjEgMTA2LjY1MSAxMy4wOTkxQzEwNy4xNDkgMTIuNTkyMSAxMDcuODczIDEyLjMzODYgMTA4LjgyMiAxMi4zMzg2QzEwOS40NjMgMTIuMzM4NiAxMDkuOTk4IDEyLjQ1MTMgMTEwLjQyNyAxMi42NzY2QzExMC44NTYgMTIuOTAyIDExMS4xNzcgMTMuMjIyNiAxMTEuMzg5IDEzLjYzODZDMTExLjYwNiAxNC4wNTAzIDExMS43MTQgMTQuNTQ0MyAxMTEuNzE0IDE1LjEyMDZWMTUuMzQ4MUMxMTEuNzE0IDE1LjUwNDEgMTExLjcwNSAxNS42NjQ1IDExMS42ODggMTUuODI5MUMxMTEuNjc1IDE1Ljk4OTUgMTExLjY1NiAxNi4xNDExIDExMS42MyAxNi4yODQxSDExMC4xMjJDMTEwLjEzNSAxNi4wNDU4IDExMC4xNDEgMTUuODIwNSAxMTAuMTQxIDE1LjYwODFDMTEwLjE0NSAxNS4zOTE1IDExMC4xNDggMTUuMTk2NSAxMTAuMTQ4IDE1LjAyMzFDMTEwLjE0OCAxNC43MjQxIDExMC4xIDE0LjQ3MDYgMTEwLjAwNSAxNC4yNjI2QzEwOS45MDkgMTQuMDUwMyAxMDkuNzY0IDEzLjg5IDEwOS41NjkgMTMuNzgxNkMxMDkuMzc0IDEzLjY3MzMgMTA5LjEyNSAxMy42MTkxIDEwOC44MjIgMTMuNjE5MUMxMDguMzc1IDEzLjYxOTEgMTA4LjA0NiAxMy43NDI2IDEwNy44MzQgMTMuOTg5NkMxMDcuNjIxIDE0LjIzNjYgMTA3LjUxNSAxNC41ODc2IDEwNy41MTUgMTUuMDQyNlYxNS42MzQxTDEwNy41MjIgMTUuODIyNlYxNi4zMjMxQzEwNy41MjIgMTYuNTIyNSAxMDcuNTUyIDE2LjcwNjYgMTA3LjYxMyAxNi44NzU2QzEwNy42NzggMTcuMDQ0NiAxMDcuNzg0IDE3LjE5MiAxMDcuOTMxIDE3LjMxNzZDMTA4LjA3OCAxNy40MzkgMTA4LjI3MyAxNy41MzQzIDEwOC41MTYgMTcuNjAzNkMxMDguNzYzIDE3LjY3MyAxMDkuMDcxIDE3LjcwNzYgMTA5LjQzOSAxNy43MDc2QzEwOS44MzggMTcuNzA3NiAxMTAuMjE3IDE3LjY2NDMgMTEwLjU3NyAxNy41Nzc2QzExMC45NDEgMTcuNDg2NiAxMTEuMjg1IDE3LjM2NTMgMTExLjYxIDE3LjIxMzZMMTExLjQ2NyAxOC41MjY2QzExMS4xNzcgMTguNjg3IDExMC44MzQgMTguODEyNiAxMTAuNDQgMTguOTAzNkMxMTAuMDUgMTguOTk5IDEwOS42MTcgMTkuMDQ2NiAxMDkuMTQgMTkuMDQ2NlpNMTA2Ljc4NyAxNi4yODQxVjE1LjE3OTFIMTExLjI5MlYxNi4yODQxSDEwNi43ODdaTTExNy4wMzggMTguODkwNlYxNS4wNjIxQzExNy4wMzggMTQuNzkzNSAxMTcuMDAxIDE0LjU2MTYgMTE2LjkyOCAxNC4zNjY2QzExNi44NTggMTQuMTcxNiAxMTYuNzQxIDE0LjAyIDExNi41NzcgMTMuOTExNkMxMTYuNDEyIDEzLjgwMzMgMTE2LjE4NyAxMy43NDkxIDExNS45MDEgMTMuNzQ5MUMxMTUuNjQ5IDEzLjc0OTEgMTE1LjQyOCAxMy43OTQ2IDExNS4yMzggMTMuODg1NkMxMTUuMDUxIDEzLjk3NjYgMTE0Ljg5OCAxNC4xMDAxIDExNC43NzYgMTQuMjU2MUMxMTQuNjU5IDE0LjQwNzggMTE0LjU3IDE0LjU4MTEgMTE0LjUxIDE0Ljc3NjFMMTE0LjI1IDEzLjg2NjFIMTE0LjU2MkMxMTQuNjMxIDEzLjU4NDUgMTE0Ljc0NiAxMy4zMzEgMTE0LjkwNiAxMy4xMDU2QzExNS4wNzEgMTIuODgwMyAxMTUuMjkyIDEyLjcwMjYgMTE1LjU2OSAxMi41NzI2QzExNS44NTEgMTIuNDM4MyAxMTYuMjAyIDEyLjM3MTEgMTE2LjYyMiAxMi4zNzExQzExNy4xMTIgMTIuMzcxMSAxMTcuNTA4IDEyLjQ2NDMgMTE3LjgxMiAxMi42NTA2QzExOC4xMTUgMTIuODMyNiAxMTguMzM4IDEzLjEwNTYgMTE4LjQ4MSAxMy40Njk2QzExOC42MjkgMTMuODMzNiAxMTguNzAyIDE0LjI4NDMgMTE4LjcwMiAxNC44MjE2VjE4Ljg5MDZIMTE3LjAzOFpNMTEyLjg4NSAxOC44OTA2VjEyLjUxNDFIMTE0LjU0OUwxMTQuNDg0IDE0LjA2NzZMMTE0LjU0OSAxNC4yMDQxVjE4Ljg5MDZIMTEyLjg4NVpNMTIyLjU0IDE5LjAyNzFDMTIxLjk5OSAxOS4wMjcxIDEyMS41NjUgMTguOTQ3IDEyMS4yNCAxOC43ODY2QzEyMC45MiAxOC42MjIgMTIwLjY4OCAxOC4zNzUgMTIwLjU0NSAxOC4wNDU2QzEyMC40MDIgMTcuNzE2MyAxMjAuMzMgMTcuMzExMSAxMjAuMzMgMTYuODMwMVYxMy4xMTIxSDEyMS45ODFWMTYuNTgzMUMxMjEuOTgxIDE2LjkyOTggMTIyLjA1OSAxNy4xODU1IDEyMi4yMTUgMTcuMzUwMUMxMjIuMzc2IDE3LjUxMDUgMTIyLjY1NSAxNy41OTA2IDEyMy4wNTQgMTcuNTkwNkMxMjMuMjg4IDE3LjU5MDYgMTIzLjUxMyAxNy41NjY4IDEyMy43MyAxNy41MTkxQzEyMy45NDYgMTcuNDY3MSAxMjQuMTQ2IDE3LjQgMTI0LjMyOCAxNy4zMTc2TDEyNC4xODUgMTguNzA4NkMxMjMuOTY4IDE4LjgwODMgMTIzLjcxOSAxOC44ODYzIDEyMy40MzcgMTguOTQyNkMxMjMuMTYgMTguOTk5IDEyMi44NjEgMTkuMDI3MSAxMjIuNTQgMTkuMDI3MVpNMTE5LjQwNyAxMy44NzkxVjEyLjU2NjFIMTI0LjI2OUwxMjQuMTI2IDEzLjg3OTFIMTE5LjQwN1pNMTIwLjM1IDEyLjY4OTZMMTIwLjM0MyAxMC45OTk2TDEyMi4wMDEgMTAuODMwNkwxMjEuOTM2IDEyLjY4OTZIMTIwLjM1Wk0xMzAuNzIzIDE5LjA2NjFDMTI5LjczNSAxOS4wNjYxIDEyOC45ODUgMTguODEyNiAxMjguNDc0IDE4LjMwNTZDMTI3Ljk2NyAxNy43OTg2IDEyNy43MTQgMTcuMDc3MSAxMjcuNzE0IDE2LjE0MTFWMTUuMjc2NkMxMjcuNzE0IDE0LjMzNjMgMTI3Ljk2NyAxMy42MTI2IDEyOC40NzQgMTMuMTA1NkMxMjguOTg1IDEyLjU5NDMgMTI5LjczNSAxMi4zMzg2IDEzMC43MjMgMTIuMzM4NkMxMzEuNzA3IDEyLjMzODYgMTMyLjQ1MiAxMi41OTQzIDEzMi45NTkgMTMuMTA1NkMxMzMuNDY2IDEzLjYxMjYgMTMzLjcyIDE0LjMzNjMgMTMzLjcyIDE1LjI3NjZWMTYuMTQxMUMxMzMuNzIgMTcuMDc3MSAxMzMuNDY2IDE3Ljc5ODYgMTMyLjk1OSAxOC4zMDU2QzEzMi40NTYgMTguODEyNiAxMzEuNzExIDE5LjA2NjEgMTMwLjcyMyAxOS4wNjYxWk0xMzAuNzIzIDE3LjczMzZDMTMxLjE1NiAxNy43MzM2IDEzMS40ODYgMTcuNjAzNiAxMzEuNzExIDE3LjM0MzZDMTMxLjk0MSAxNy4wODM2IDEzMi4wNTYgMTYuNzExIDEzMi4wNTYgMTYuMjI1NlYxNS4xOTIxQzEzMi4wNTYgMTQuNjk4MSAxMzEuOTQxIDE0LjMyMTEgMTMxLjcxMSAxNC4wNjExQzEzMS40ODYgMTMuNzk2OCAxMzEuMTU2IDEzLjY2NDYgMTMwLjcyMyAxMy42NjQ2QzEzMC4yODUgMTMuNjY0NiAxMjkuOTUyIDEzLjc5NjggMTI5LjcyMiAxNC4wNjExQzEyOS40OTcgMTQuMzIxMSAxMjkuMzg0IDE0LjY5ODEgMTI5LjM4NCAxNS4xOTIxVjE2LjIyNTZDMTI5LjM4NCAxNi43MTEgMTI5LjQ5NyAxNy4wODM2IDEyOS43MjIgMTcuMzQzNkMxMjkuOTUyIDE3LjYwMzYgMTMwLjI4NSAxNy43MzM2IDEzMC43MjMgMTcuNzMzNlpNMTM3LjE1NCAxMC4wOTYxQzEzNy40OTIgMTAuMDk2MSAxMzcuODA0IDEwLjEyNDMgMTM4LjA5IDEwLjE4MDZDMTM4LjM3NiAxMC4yMzcgMTM4LjYyOSAxMC4zMDYzIDEzOC44NSAxMC4zODg2TDEzOSAxMS42MzY2QzEzOC44MTMgMTEuNTgwMyAxMzguNjE4IDExLjUzNDggMTM4LjQxNSAxMS41MDAxQzEzOC4yMTUgMTEuNDYxMSAxMzcuOTk0IDExLjQ0MTYgMTM3Ljc1MiAxMS40NDE2QzEzNy40NjEgMTEuNDQxNiAxMzcuMjMyIDExLjQ3NDEgMTM3LjA2MyAxMS41MzkxQzEzNi44OTggMTEuNjA0MSAxMzYuNzgxIDExLjY5NzMgMTM2LjcxMiAxMS44MTg2QzEzNi42NDIgMTEuOTQgMTM2LjYwOCAxMi4wODMgMTM2LjYwOCAxMi4yNDc2VjEyLjI2NzFDMTM2LjYwOCAxMi4zODQxIDEzNi42MjUgMTIuNDk0NiAxMzYuNjYgMTIuNTk4NkMxMzYuNjk0IDEyLjcwMjYgMTM2LjczNSAxMi43OTU4IDEzNi43ODMgMTIuODc4MUwxMzUuNjk4IDEyLjkxNzFWMTIuNzQxNkMxMzUuNDk4IDEyLjY0NjMgMTM1LjMyOSAxMi41MDU1IDEzNS4xOTEgMTIuMzE5MUMxMzUuMDUyIDEyLjEzMjggMTM0Ljk4MyAxMS45MDEgMTM0Ljk4MyAxMS42MjM2VjExLjU5MTFDMTM0Ljk4MyAxMS4xMzE4IDEzNS4xNTggMTAuNzY3OCAxMzUuNTA5IDEwLjQ5OTFDMTM1Ljg2NCAxMC4yMzA1IDEzNi40MTMgMTAuMDk2MSAxMzcuMTU0IDEwLjA5NjFaTTEzNS4yMDQgMTguODkwNlYxMy4yNjE2SDEzNi44NjFWMTguODkwNkgxMzUuMjA0Wk0xMzQuMjgxIDE0LjEwNjZWMTIuNzg3MUwxMzUuODkzIDEyLjgwMDFMMTM2LjU0MyAxMi43ODcxSDEzOC45ODdMMTM4Ljg0NCAxNC4xMDY2SDEzNC4yODFaTTU2LjExNzUgMzEuODkwNkw1Ni4zOTA1IDIzLjU4MzZINTguODQ3NUw2MC40MDc1IDI4Ljg2ODFINjAuNDkyTDYyLjA0NTUgMjMuNTgzNkg2NC40OTZMNjQuNzc1NSAzMS44OTA2SDYzLjEyNDVMNjMuMDQ2NSAyOC45MDA2TDYyLjk3NSAyNS40NDkxSDYyLjg3MUw2MS4yMzk1IDMwLjkyMjFINTkuNjUzNUw1OC4wMTU1IDI1LjQ0OTFINTcuOTExNUw1Ny44NCAyOC45MDcxTDU3Ljc2ODUgMzEuODkwNkg1Ni4xMTc1Wk02OC45NjA0IDMyLjA2NjFDNjcuOTcyNCAzMi4wNjYxIDY3LjIyMjcgMzEuODEyNiA2Ni43MTE0IDMxLjMwNTZDNjYuMjA0NCAzMC43OTg2IDY1Ljk1MDkgMzAuMDc3MSA2NS45NTA5IDI5LjE0MTFWMjguMjc2NkM2NS45NTA5IDI3LjMzNjMgNjYuMjA0NCAyNi42MTI2IDY2LjcxMTQgMjYuMTA1NkM2Ny4yMjI3IDI1LjU5NDMgNjcuOTcyNCAyNS4zMzg2IDY4Ljk2MDQgMjUuMzM4NkM2OS45NDQgMjUuMzM4NiA3MC42ODk0IDI1LjU5NDMgNzEuMTk2NCAyNi4xMDU2QzcxLjcwMzQgMjYuNjEyNiA3MS45NTY5IDI3LjMzNjMgNzEuOTU2OSAyOC4yNzY2VjI5LjE0MTFDNzEuOTU2OSAzMC4wNzcxIDcxLjcwMzQgMzAuNzk4NiA3MS4xOTY0IDMxLjMwNTZDNzAuNjkzNyAzMS44MTI2IDY5Ljk0ODQgMzIuMDY2MSA2OC45NjA0IDMyLjA2NjFaTTY4Ljk2MDQgMzAuNzMzNkM2OS4zOTM3IDMwLjczMzYgNjkuNzIzIDMwLjYwMzYgNjkuOTQ4NCAzMC4zNDM2QzcwLjE3OCAzMC4wODM2IDcwLjI5MjkgMjkuNzExIDcwLjI5MjkgMjkuMjI1NlYyOC4xOTIxQzcwLjI5MjkgMjcuNjk4MSA3MC4xNzggMjcuMzIxMSA2OS45NDg0IDI3LjA2MTFDNjkuNzIzIDI2Ljc5NjggNjkuMzkzNyAyNi42NjQ2IDY4Ljk2MDQgMjYuNjY0NkM2OC41MjI3IDI2LjY2NDYgNjguMTg5IDI2Ljc5NjggNjcuOTU5NCAyNy4wNjExQzY3LjczNCAyNy4zMjExIDY3LjYyMTQgMjcuNjk4MSA2Ny42MjE0IDI4LjE5MjFWMjkuMjI1NkM2Ny42MjE0IDI5LjcxMSA2Ny43MzQgMzAuMDgzNiA2Ny45NTk0IDMwLjM0MzZDNjguMTg5IDMwLjYwMzYgNjguNTIyNyAzMC43MzM2IDY4Ljk2MDQgMzAuNzMzNlpNNzUuNjA1NyAzMi4wMjcxQzc1LjA2NCAzMi4wMjcxIDc0LjYzMDcgMzEuOTQ3IDc0LjMwNTcgMzEuNzg2NkM3My45ODUgMzEuNjIyIDczLjc1MzIgMzEuMzc1IDczLjYxMDIgMzEuMDQ1NkM3My40NjcyIDMwLjcxNjMgNzMuMzk1NyAzMC4zMTExIDczLjM5NTcgMjkuODMwMVYyNi4xMTIxSDc1LjA0NjdWMjkuNTgzMUM3NS4wNDY3IDI5LjkyOTggNzUuMTI0NyAzMC4xODU1IDc1LjI4MDcgMzAuMzUwMUM3NS40NDEgMzAuNTEwNSA3NS43MjA1IDMwLjU5MDYgNzYuMTE5MiAzMC41OTA2Qzc2LjM1MzIgMzAuNTkwNiA3Ni41Nzg1IDMwLjU2NjggNzYuNzk1MiAzMC41MTkxQzc3LjAxMTkgMzAuNDY3MSA3Ny4yMTEyIDMwLjQgNzcuMzkzMiAzMC4zMTc2TDc3LjI1MDIgMzEuNzA4NkM3Ny4wMzM1IDMxLjgwODMgNzYuNzg0NCAzMS44ODYzIDc2LjUwMjcgMzEuOTQyNkM3Ni4yMjU0IDMxLjk5OSA3NS45MjY0IDMyLjAyNzEgNzUuNjA1NyAzMi4wMjcxWk03Mi40NzI3IDI2Ljg3OTFWMjUuNTY2MUg3Ny4zMzQ3TDc3LjE5MTcgMjYuODc5MUg3Mi40NzI3Wk03My40MTUyIDI1LjY4OTZMNzMuNDA4NyAyMy45OTk2TDc1LjA2NjIgMjMuODMwNkw3NS4wMDEyIDI1LjY4OTZINzMuNDE1MlpNODEuMDA4MiAzMi4wNjYxQzgwLjAyMDIgMzIuMDY2MSA3OS4yNzA2IDMxLjgxMjYgNzguNzU5MiAzMS4zMDU2Qzc4LjI1MjIgMzAuNzk4NiA3Ny45OTg3IDMwLjA3NzEgNzcuOTk4NyAyOS4xNDExVjI4LjI3NjZDNzcuOTk4NyAyNy4zMzYzIDc4LjI1MjIgMjYuNjEyNiA3OC43NTkyIDI2LjEwNTZDNzkuMjcwNiAyNS41OTQzIDgwLjAyMDIgMjUuMzM4NiA4MS4wMDgyIDI1LjMzODZDODEuOTkxOSAyNS4zMzg2IDgyLjczNzIgMjUuNTk0MyA4My4yNDQyIDI2LjEwNTZDODMuNzUxMiAyNi42MTI2IDg0LjAwNDcgMjcuMzM2MyA4NC4wMDQ3IDI4LjI3NjZWMjkuMTQxMUM4NC4wMDQ3IDMwLjA3NzEgODMuNzUxMiAzMC43OTg2IDgzLjI0NDIgMzEuMzA1NkM4Mi43NDE2IDMxLjgxMjYgODEuOTk2MiAzMi4wNjYxIDgxLjAwODIgMzIuMDY2MVpNODEuMDA4MiAzMC43MzM2QzgxLjQ0MTYgMzAuNzMzNiA4MS43NzA5IDMwLjYwMzYgODEuOTk2MiAzMC4zNDM2QzgyLjIyNTkgMzAuMDgzNiA4Mi4zNDA3IDI5LjcxMSA4Mi4zNDA3IDI5LjIyNTZWMjguMTkyMUM4Mi4zNDA3IDI3LjY5ODEgODIuMjI1OSAyNy4zMjExIDgxLjk5NjIgMjcuMDYxMUM4MS43NzA5IDI2Ljc5NjggODEuNDQxNiAyNi42NjQ2IDgxLjAwODIgMjYuNjY0NkM4MC41NzA2IDI2LjY2NDYgODAuMjM2OSAyNi43OTY4IDgwLjAwNzIgMjcuMDYxMUM3OS43ODE5IDI3LjMyMTEgNzkuNjY5MiAyNy42OTgxIDc5LjY2OTIgMjguMTkyMVYyOS4yMjU2Qzc5LjY2OTIgMjkuNzExIDc5Ljc4MTkgMzAuMDgzNiA4MC4wMDcyIDMwLjM0MzZDODAuMjM2OSAzMC42MDM2IDgwLjU3MDYgMzAuNzMzNiA4MS4wMDgyIDMwLjczMzZaTTg2Ljg0MDIgMjguMTg1Nkw4Ni40MTc3IDI3LjA3NDFIODYuODIwN0M4Ni45Mzc3IDI2LjU1ODUgODcuMTUgMjYuMTUzMyA4Ny40NTc3IDI1Ljg1ODZDODcuNzY1MyAyNS41NjQgODguMTkyMiAyNS40MTY2IDg4LjczODIgMjUuNDE2NkM4OC44NTA4IDI1LjQxNjYgODguOTUyNyAyNS40MjUzIDg5LjA0MzcgMjUuNDQyNkM4OS4xMzQ3IDI1LjQ1NTYgODkuMjE3IDI1LjQ3MyA4OS4yOTA3IDI1LjQ5NDZMODkuMzgxNyAyNy4xNTg2Qzg5LjI4NjMgMjcuMTI4MyA4OS4xNzU4IDI3LjEwNjYgODkuMDUwMiAyNy4wOTM2Qzg4LjkyNDUgMjcuMDc2MyA4OC43OTIzIDI3LjA2NzYgODguNjUzNyAyNy4wNjc2Qzg4LjIxMTcgMjcuMDY3NiA4Ny44MzQ3IDI3LjE2NTEgODcuNTIyNyAyNy4zNjAxQzg3LjIxNSAyNy41NTUxIDg2Ljk4NzUgMjcuODMwMyA4Ni44NDAyIDI4LjE4NTZaTTg1LjIyMTcgMzEuODkwNlYyNS41MTQxSDg2LjgwNzdMODYuNzM2MiAyNy40NzA2TDg2Ljg4NTcgMjcuNTI5MVYzMS44OTA2SDg1LjIyMTdaTTk0Ljc2NzIgMzEuODkwNkw5Mi40MDc3IDIzLjU4MzZIOTQuMTc1N0w5NS45OTU3IDMwLjYxNjZIOTYuMTQ1Mkw5Ny45NjUyIDIzLjU4MzZIOTkuNzMzMkw5Ny4zODAyIDMxLjg5MDZIOTQuNzY3MlpNMTAzLjA0NiAzMi4wNDY2QzEwMS45NTQgMzIuMDQ2NiAxMDEuMTQyIDMxLjc5NTMgMTAwLjYwOSAzMS4yOTI2QzEwMC4wNzYgMzAuNzkgOTkuODA5MyAzMC4wNjIgOTkuODA5MyAyOS4xMDg2VjI4LjI3NjZDOTkuODA5MyAyNy4zMzIgMTAwLjA1OCAyNi42MDYxIDEwMC41NTcgMjYuMDk5MUMxMDEuMDU1IDI1LjU5MjEgMTAxLjc3OSAyNS4zMzg2IDEwMi43MjggMjUuMzM4NkMxMDMuMzY5IDI1LjMzODYgMTAzLjkwNCAyNS40NTEzIDEwNC4zMzMgMjUuNjc2NkMxMDQuNzYyIDI1LjkwMiAxMDUuMDgzIDI2LjIyMjYgMTA1LjI5NSAyNi42Mzg2QzEwNS41MTIgMjcuMDUwMyAxMDUuNjIgMjcuNTQ0MyAxMDUuNjIgMjguMTIwNlYyOC4zNDgxQzEwNS42MiAyOC41MDQxIDEwNS42MTIgMjguNjY0NSAxMDUuNTk0IDI4LjgyOTFDMTA1LjU4MSAyOC45ODk1IDEwNS41NjIgMjkuMTQxMSAxMDUuNTM2IDI5LjI4NDFIMTA0LjAyOEMxMDQuMDQxIDI5LjA0NTggMTA0LjA0NyAyOC44MjA1IDEwNC4wNDcgMjguNjA4MUMxMDQuMDUyIDI4LjM5MTUgMTA0LjA1NCAyOC4xOTY1IDEwNC4wNTQgMjguMDIzMUMxMDQuMDU0IDI3LjcyNDEgMTA0LjAwNiAyNy40NzA2IDEwMy45MTEgMjcuMjYyNkMxMDMuODE1IDI3LjA1MDMgMTAzLjY3IDI2Ljg5IDEwMy40NzUgMjYuNzgxNkMxMDMuMjggMjYuNjczMyAxMDMuMDMxIDI2LjYxOTEgMTAyLjcyOCAyNi42MTkxQzEwMi4yODEgMjYuNjE5MSAxMDEuOTUyIDI2Ljc0MjYgMTAxLjc0IDI2Ljk4OTZDMTAxLjUyNyAyNy4yMzY2IDEwMS40MjEgMjcuNTg3NiAxMDEuNDIxIDI4LjA0MjZWMjguNjM0MUwxMDEuNDI4IDI4LjgyMjZWMjkuMzIzMUMxMDEuNDI4IDI5LjUyMjUgMTAxLjQ1OCAyOS43MDY2IDEwMS41MTkgMjkuODc1NkMxMDEuNTg0IDMwLjA0NDYgMTAxLjY5IDMwLjE5MiAxMDEuODM3IDMwLjMxNzZDMTAxLjk4NSAzMC40MzkgMTAyLjE4IDMwLjUzNDMgMTAyLjQyMiAzMC42MDM2QzEwMi42NjkgMzAuNjczIDEwMi45NzcgMzAuNzA3NiAxMDMuMzQ1IDMwLjcwNzZDMTAzLjc0NCAzMC43MDc2IDEwNC4xMjMgMzAuNjY0MyAxMDQuNDgzIDMwLjU3NzZDMTA0Ljg0NyAzMC40ODY2IDEwNS4xOTEgMzAuMzY1MyAxMDUuNTE2IDMwLjIxMzZMMTA1LjM3MyAzMS41MjY2QzEwNS4wODMgMzEuNjg3IDEwNC43NDEgMzEuODEyNiAxMDQuMzQ2IDMxLjkwMzZDMTAzLjk1NiAzMS45OTkgMTAzLjUyMyAzMi4wNDY2IDEwMy4wNDYgMzIuMDQ2NlpNMTAwLjY5MyAyOS4yODQxVjI4LjE3OTFIMTA1LjE5OFYyOS4yODQxSDEwMC42OTNaTTExMC45NDQgMzEuODkwNlYyOC4wNjIxQzExMC45NDQgMjcuNzkzNSAxMTAuOTA4IDI3LjU2MTYgMTEwLjgzNCAyNy4zNjY2QzExMC43NjUgMjcuMTcxNiAxMTAuNjQ4IDI3LjAyIDExMC40ODMgMjYuOTExNkMxMTAuMzE4IDI2LjgwMzMgMTEwLjA5MyAyNi43NDkxIDEwOS44MDcgMjYuNzQ5MUMxMDkuNTU2IDI2Ljc0OTEgMTA5LjMzNyAyNi43OTQ2IDEwOS4xNSAyNi44ODU2QzEwOC45NjQgMjYuOTc2NiAxMDguODEgMjcuMTAwMSAxMDguNjg5IDI3LjI1NjFDMTA4LjU3MiAyNy40MDc4IDEwOC40ODUgMjcuNTgxMSAxMDguNDI5IDI3Ljc3NjFMMTA4LjA5MSAyNi44NjYxSDEwOC40ODdDMTA4LjU2MSAyNi41ODQ1IDEwOC42NzggMjYuMzMxIDEwOC44MzggMjYuMTA1NkMxMDguOTk5IDI1Ljg4MDMgMTA5LjIxOCAyNS43MDI2IDEwOS40OTUgMjUuNTcyNkMxMDkuNzcyIDI1LjQzODMgMTEwLjExNyAyNS4zNzExIDExMC41MjggMjUuMzcxMUMxMTEuMDE4IDI1LjM3MTEgMTExLjQxNSAyNS40NjQzIDExMS43MTggMjUuNjUwNkMxMTIuMDIxIDI1LjgzMjYgMTEyLjI0NCAyNi4xMDU2IDExMi4zODcgMjYuNDY5NkMxMTIuNTM1IDI2LjgzMzYgMTEyLjYwOCAyNy4yODQzIDExMi42MDggMjcuODIxNlYzMS44OTA2SDExMC45NDRaTTEwNi43OTEgMzEuODkwNlYyMy4yOTc2SDEwOC40NDhWMjUuMjYwNkwxMDguNDE2IDI3LjI0MzFMMTA4LjQ1NSAyNy4zNjY2VjMxLjg5MDZIMTA2Ljc5MVpNMTE0LjAxNSAzMS44OTA2VjI1LjUxNDFIMTE1LjY3OVYzMS44OTA2SDExNC4wMTVaTTExNC44NDcgMjQuNzYwMUMxMTQuNTMxIDI0Ljc2MDEgMTE0LjI5NyAyNC42ODY1IDExNC4xNDUgMjQuNTM5MUMxMTMuOTk4IDI0LjM4NzUgMTEzLjkyNCAyNC4xNzk1IDExMy45MjQgMjMuOTE1MVYyMy44ODI2QzExMy45MjQgMjMuNjE4MyAxMTMuOTk4IDIzLjQxMDMgMTE0LjE0NSAyMy4yNTg2QzExNC4yOTcgMjMuMTA3IDExNC41MzEgMjMuMDMxMSAxMTQuODQ3IDIzLjAzMTFDMTE1LjE1OSAyMy4wMzExIDExNS4zOTEgMjMuMTA3IDExNS41NDIgMjMuMjU4NkMxMTUuNjk0IDIzLjQxMDMgMTE1Ljc3IDIzLjYxODMgMTE1Ljc3IDIzLjg4MjZWMjMuOTE1MUMxMTUuNzcgMjQuMTgzOCAxMTUuNjk0IDI0LjM5MTggMTE1LjU0MiAyNC41MzkxQzExNS4zOTEgMjQuNjg2NSAxMTUuMTU5IDI0Ljc2MDEgMTE0Ljg0NyAyNC43NjAxWk0xMTkuNzk2IDMyLjA1MzFDMTE4LjgxMyAzMi4wNTMxIDExOC4wODIgMzEuNzkzMSAxMTcuNjA2IDMxLjI3MzFDMTE3LjEzMyAzMC43NTMxIDExNi44OTcgMzAuMDIzIDExNi44OTcgMjkuMDgyNlYyOC4zMDI2QzExNi44OTcgMjcuMzY2NiAxMTcuMTM2IDI2LjY0MDggMTE3LjYxMiAyNi4xMjUxQzExOC4wODkgMjUuNjA5NSAxMTguODE3IDI1LjM1MTYgMTE5Ljc5NiAyNS4zNTE2QzEyMC4wNTIgMjUuMzUxNiAxMjAuMjkgMjUuMzczMyAxMjAuNTExIDI1LjQxNjZDMTIwLjczNyAyNS40NTU2IDEyMC45NCAyNS41MDk4IDEyMS4xMjIgMjUuNTc5MUMxMjEuMzA5IDI1LjY0ODUgMTIxLjQ3MSAyNS43MjIxIDEyMS42MSAyNS44MDAxTDEyMS43NDYgMjcuMTk3NkMxMjEuNTM0IDI3LjA2MzMgMTIxLjI5NiAyNi45NTA2IDEyMS4wMzEgMjYuODU5NkMxMjAuNzcxIDI2Ljc2ODYgMTIwLjQ3IDI2LjcyMzEgMTIwLjEyOCAyNi43MjMxQzExOS41OSAyNi43MjMxIDExOS4xOTYgMjYuODYxOCAxMTguOTQ1IDI3LjEzOTFDMTE4LjY5OCAyNy40MTIxIDExOC41NzQgMjcuODEwOCAxMTguNTc0IDI4LjMzNTFWMjkuMDI0MUMxMTguNTc0IDI5LjU0NDEgMTE4LjcwMiAyOS45NDUgMTE4Ljk1OCAzMC4yMjY2QzExOS4yMTggMzAuNTA4MyAxMTkuNjE2IDMwLjY0OTEgMTIwLjE1NCAzMC42NDkxQzEyMC40OTYgMzAuNjQ5MSAxMjAuNzk5IDMwLjYwNTggMTIxLjA2NCAzMC41MTkxQzEyMS4zMjggMzAuNDI4MSAxMjEuNTc1IDMwLjMxNzYgMTIxLjgwNSAzMC4xODc2TDEyMS42NjggMzEuNTkxNkMxMjEuNDU2IDMxLjcxMyAxMjEuMTg5IDMxLjgxOTEgMTIwLjg2OSAzMS45MTAxQzEyMC41NDggMzIuMDA1NSAxMjAuMTkxIDMyLjA1MzEgMTE5Ljc5NiAzMi4wNTMxWk0xMjIuOTAyIDMxLjg5MDZWMjMuMjk3NkgxMjQuNTcyVjMxLjg5MDZIMTIyLjkwMlpNMTI5LjAzNCAzMi4wNDY2QzEyNy45NDIgMzIuMDQ2NiAxMjcuMTI5IDMxLjc5NTMgMTI2LjU5NiAzMS4yOTI2QzEyNi4wNjMgMzAuNzkgMTI1Ljc5NyAzMC4wNjIgMTI1Ljc5NyAyOS4xMDg2VjI4LjI3NjZDMTI1Ljc5NyAyNy4zMzIgMTI2LjA0NiAyNi42MDYxIDEyNi41NDQgMjYuMDk5MUMxMjcuMDQyIDI1LjU5MjEgMTI3Ljc2NiAyNS4zMzg2IDEyOC43MTUgMjUuMzM4NkMxMjkuMzU2IDI1LjMzODYgMTI5Ljg5MiAyNS40NTEzIDEzMC4zMjEgMjUuNjc2NkMxMzAuNzUgMjUuOTAyIDEzMS4wNyAyNi4yMjI2IDEzMS4yODMgMjYuNjM4NkMxMzEuNDk5IDI3LjA1MDMgMTMxLjYwOCAyNy41NDQzIDEzMS42MDggMjguMTIwNlYyOC4zNDgxQzEzMS42MDggMjguNTA0MSAxMzEuNTk5IDI4LjY2NDUgMTMxLjU4MiAyOC44MjkxQzEzMS41NjkgMjguOTg5NSAxMzEuNTQ5IDI5LjE0MTEgMTMxLjUyMyAyOS4yODQxSDEzMC4wMTVDMTMwLjAyOCAyOS4wNDU4IDEzMC4wMzUgMjguODIwNSAxMzAuMDM1IDI4LjYwODFDMTMwLjAzOSAyOC4zOTE1IDEzMC4wNDEgMjguMTk2NSAxMzAuMDQxIDI4LjAyMzFDMTMwLjA0MSAyNy43MjQxIDEyOS45OTMgMjcuNDcwNiAxMjkuODk4IDI3LjI2MjZDMTI5LjgwMyAyNy4wNTAzIDEyOS42NTggMjYuODkgMTI5LjQ2MyAyNi43ODE2QzEyOS4yNjggMjYuNjczMyAxMjkuMDE4IDI2LjYxOTEgMTI4LjcxNSAyNi42MTkxQzEyOC4yNjkgMjYuNjE5MSAxMjcuOTM5IDI2Ljc0MjYgMTI3LjcyNyAyNi45ODk2QzEyNy41MTUgMjcuMjM2NiAxMjcuNDA5IDI3LjU4NzYgMTI3LjQwOSAyOC4wNDI2VjI4LjYzNDFMMTI3LjQxNSAyOC44MjI2VjI5LjMyMzFDMTI3LjQxNSAyOS41MjI1IDEyNy40NDUgMjkuNzA2NiAxMjcuNTA2IDI5Ljg3NTZDMTI3LjU3MSAzMC4wNDQ2IDEyNy42NzcgMzAuMTkyIDEyNy44MjUgMzAuMzE3NkMxMjcuOTcyIDMwLjQzOSAxMjguMTY3IDMwLjUzNDMgMTI4LjQxIDMwLjYwMzZDMTI4LjY1NyAzMC42NzMgMTI4Ljk2NCAzMC43MDc2IDEyOS4zMzMgMzAuNzA3NkMxMjkuNzMxIDMwLjcwNzYgMTMwLjExIDMwLjY2NDMgMTMwLjQ3IDMwLjU3NzZDMTMwLjgzNCAzMC40ODY2IDEzMS4xNzkgMzAuMzY1MyAxMzEuNTA0IDMwLjIxMzZMMTMxLjM2MSAzMS41MjY2QzEzMS4wNyAzMS42ODcgMTMwLjcyOCAzMS44MTI2IDEzMC4zMzQgMzEuOTAzNkMxMjkuOTQ0IDMxLjk5OSAxMjkuNTEgMzIuMDQ2NiAxMjkuMDM0IDMyLjA0NjZaTTEyNi42ODEgMjkuMjg0MVYyOC4xNzkxSDEzMS4xODVWMjkuMjg0MUgxMjYuNjgxWk0xMzQuOTk0IDMyLjA0NjZDMTM0LjQ4MyAzMi4wNDY2IDEzNC4wMjggMzEuOTk2OCAxMzMuNjI5IDMxLjg5NzFDMTMzLjIzNSAzMS44MDE4IDEzMi44OTkgMzEuNjkzNSAxMzIuNjIxIDMxLjU3MjFMMTMyLjQ3MiAzMC4xMjkxQzEzMi44MDEgMzAuMjgwOCAxMzMuMTYzIDMwLjQxMyAxMzMuNTU3IDMwLjUyNTZDMTMzLjk1NiAzMC42MzgzIDEzNC4zOTQgMzAuNjk0NiAxMzQuODcgMzAuNjk0NkMxMzUuMjg2IDMwLjY5NDYgMTM1LjU4OCAzMC42NDI2IDEzNS43NzQgMzAuNTM4NkMxMzUuOTYgMzAuNDMwMyAxMzYuMDUzIDMwLjI3IDEzNi4wNTMgMzAuMDU3NlYzMC4wMTg2QzEzNi4wNTMgMjkuODc1NiAxMzYuMDEgMjkuNzU4NiAxMzUuOTIzIDI5LjY2NzZDMTM1Ljg0MSAyOS41NzY2IDEzNS42OTQgMjkuNDk0MyAxMzUuNDgxIDI5LjQyMDZDMTM1LjI2OSAyOS4zNDI2IDEzNC45NyAyOS4yNjAzIDEzNC41ODQgMjkuMTczNkMxMzQuMDUxIDI5LjA0OCAxMzMuNjI5IDI4LjkwMjggMTMzLjMxNyAyOC43MzgxQzEzMy4wMDkgMjguNTY5MSAxMzIuNzg4IDI4LjM2MzMgMTMyLjY1NCAyOC4xMjA2QzEzMi41MiAyNy44NzM2IDEzMi40NTIgMjcuNTc5IDEzMi40NTIgMjcuMjM2NlYyNy4xNzgxQzEzMi40NTIgMjYuNTc1OCAxMzIuNjY3IDI2LjEyMyAxMzMuMDk2IDI1LjgxOTZDMTMzLjUyNSAyNS41MTIgMTM0LjE2IDI1LjM1ODEgMTM1IDI1LjM1ODFDMTM1LjQ5OSAyNS4zNTgxIDEzNS45MzkgMjUuNDA4IDEzNi4zMiAyNS41MDc2QzEzNi43MDYgMjUuNjAzIDEzNy4wMjYgMjUuNzE3OCAxMzcuMjgyIDI1Ljg1MjFMMTM3LjQzMSAyNy4xNzgxQzEzNy4xMjggMjcuMDM1MSAxMzYuNzg4IDI2LjkxNiAxMzYuNDExIDI2LjgyMDZDMTM2LjAzNCAyNi43MjEgMTM1LjYyOSAyNi42NzExIDEzNS4xOTUgMjYuNjcxMUMxMzQuOTE0IDI2LjY3MTEgMTM0LjY5MSAyNi42OTUgMTM0LjUyNiAyNi43NDI2QzEzNC4zNjYgMjYuNzg2IDEzNC4yNTEgMjYuODQ4OCAxMzQuMTgxIDI2LjkzMTFDMTM0LjExMiAyNy4wMTM1IDEzNC4wNzcgMjcuMTEzMSAxMzQuMDc3IDI3LjIzMDFWMjcuMjYyNkMxMzQuMDc3IDI3LjM5MjYgMTM0LjExNCAyNy41MDUzIDEzNC4xODggMjcuNjAwNkMxMzQuMjY2IDI3LjY5NiAxMzQuNDA3IDI3Ljc4MjYgMTM0LjYxIDI3Ljg2MDZDMTM0LjgxNCAyNy45MzQzIDEzNS4xIDI4LjAxNDUgMTM1LjQ2OCAyOC4xMDExQzEzNi4wMDYgMjguMjEzOCAxMzYuNDM3IDI4LjM0NiAxMzYuNzYyIDI4LjQ5NzZDMTM3LjA4NyAyOC42NDkzIDEzNy4zMjMgMjguODQ0MyAxMzcuNDcgMjkuMDgyNkMxMzcuNjE4IDI5LjMxNjYgMTM3LjY5MSAyOS42MjQzIDEzNy42OTEgMzAuMDA1NlYzMC4wODM2QzEzNy42OTEgMzAuNzQyMyAxMzcuNDY4IDMxLjIzNDEgMTM3LjAyMiAzMS41NTkxQzEzNi41NzYgMzEuODg0MSAxMzUuOSAzMi4wNDY2IDEzNC45OTQgMzIuMDQ2NloiIGZpbGw9IiNGNkY2RjYiLz4KPC9zdmc+ClhAybDn2tK8ED02UtdSJzxV+NF3tMuGQLcvndUGObrYgXP9y2NBNV6vVsNmkNt4zDnadCx8uyAr93vIuAsBRMiUkGpuYW1lU3BhY2VzoXFvcmcuaXNvLjE4MDEzLjUuMZLYGFhIpGhkaWdlc3RJRAFmcmFuZG9tUF2BsFh1JOboDp6yK4r12XVxZWxlbWVudElkZW50aWZpZXJjc2V4bGVsZW1lbnRWYWx1ZWEx2BhZEtekaGRpZ2VzdElED2ZyYW5kb21QVHu6WoNegd/NwwUmKLfU6nFlbGVtZW50SWRlbnRpZmllcmhwb3J0cmFpdGxlbGVtZW50VmFsdWVZEomJUE5HDQoaCgAAAA1JSERSAAAAbwAAAI8IAwAAAMNfddAAAAAEZ0FNQQAAsY8L/GEFAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAEhQTFRFr66puYx1roJronZgDAgGHBMPlmxWqqqlo6KenJuWlJKO0aWRPSsiLR8YimJNfVdDx5mCTDcsXkIziomFbks517KgfHp1ZGFctcDraQAAEcpJREFUaN6MmolioyAQhlGjqAENaOX933Tn4lJjlu62TXN8/HMxKGrU/IX/TkPBF31TSvFPTf8uQ1//wO+/DhV5iBsrWKRFRgEuoPRXnanyWGemKskqqRPYqNPULu+V7ydCqVHr+GzEqErmqIQ1FrDCnCxRqVqvUhGbkLrkapUoZ6RiRkZeWap8dZZeSbs8jIYpuZX/tK4Uqmj76P7SCypGUWVTfZ7AjfvSr+M5PsWId7j4/NfwJKAu31hMNesbL/mQdMR3ZOvUdiwEZ3k5mlN2FZOuE0IVVrtm0kVS+mMdPzo7M06YM328U5byTkkuqOrZU4KrXCJOgVoSb2tAkecx+GP2ydROvBxeOQNOwZoccuO1OkBTaIObaeAPnexYZ8w5lnKkqtsap5J/igyil87BO2Nc2OZtm6cxl4iiYhazvZaeK690gfyLcTUdPgTnjO1NmOcjwDgmEjudBaqKlou9OieVSsKyOPHZdhxbMH3/6mFYZ+gX6w8UOl4sekp+XQXxdQUqtSFsnLbgAUEDgfydyCByVuTXM03frVoXXlEzBTaC+RzqiRACItkaY82xbZRQ03bMhTWr+q10VeQuCZ5tibBgX3GwKOMMIUGesyYc2zQHZ10I/hhjvJ6reGm1U/XKjkNDOluxQFPfO++NtUiGRwBy8RHSp5sGoK5Eqq74yczjhIZ8kb8i1Bq/G+v21aNMa2QSFof4NMxfWdmD6rTIJFz/KgZ+tjX7DtR1dc552/NfgQ3q+t44/OX4KVBdchRyIFBMJiBaDwYY04E5PUgEgSlyEAQSgQyJejzok/ZH1w6GBA9EimmARoPPWpd1X3fjLDhxd9amUAW1RIThjjA96FNF2iV1U5BPioaUB71bh2NZyXYGklKiyZJCLD9g3N7NQT/48LrEIC6Sku8oIuCn2QcAYj6izKTfsA/pVWH7GqVF35fHFNhQkt0UJug+lIBpBybFPAFInyMJnvWi0G5S5e546pwNgDOVKPzHCQY8AAERotMSJct3MS0hsvymHni6FqiPhOsxyTi1LEtFGegib0wfgb0YlHOCSlAYH3Kw9B78trk+JZuFD6byUYQNfShEq+XCDdGSUpPtifM5nvKv6upGl13CwxpTpb3l4i3J3iee+I/+6OYf/kuPNxvVGZovBMWJ96LoZG0QpS8r1ds7rqQ03KSeciJZcwwxJrGUeFzRgWcqHpiOp4Qg5uHL992J/2htHL/leyVvipMnnpOSYezJosmfOJ2e5e306rhI2vAQMZl9pPXbcKz0xOOZp+CP6Rl5qMcbEwOGBc8/7Qm5Z3hNiOlGNdhiZ2Z7G92WlvieX4Z/lVJu+9QJGP+TplVIJZhLIlUWm2oVPYpA/lTrOD0xPn2OFxS4/bToGOu0jdkQkyINicHUXRgjRcjIEiEKX8C/Xx6KB6Prk53o3UbqJhZHjyWLoyLGhJRp1i6roFgbUiJs6seYTUw9Ix9jCOJ3v8NY13VZIOx5bcXX4SSoUBvmScmmGLDH9itApz5Xeyfr+c5jXYZh6Lp2GJbduwQgyXsGRn34/WndZd72KpKPLCmygNa1TdN8Pk3TAXIFDCmj5+Frx1ixlLIpbcz4VGNwxOyLDQJ0EGDBZUFhwOLRNC0ilwW6C5rJgI9WcmwMGA66WT02FpjtsZMmFzq0Ygestm0I96ZByLaFJ8jEPFBzaVCcd3i2ptIhTY1y3C/kspYtGXGRCVSU2vIogbHsfe9jpOeM+xJZzlacP+Mi7+/9B4OJH4YKD8SuVIjychnGZ4GTOWxubVleW/H++IuJzCSVJHAYdlca1IYf8Xm4zRQN0M7ymkIeqftLEt+FRORhqphc148fvBAqHppzaGv3/dXEtwhsEs/nxfk379hcsTVZOcNv5bFhRWHiDWvBg733L3sek0+TS/qI18TwLHhR3yfrW3FJih/hfvD0HKZQdJQ7lbBa3zvSBFfrI176BHc8Zh+u7vNx5rH3LgZ956xI+hbgxU0TfsLD+iDXK8btKPeW3/UV4RIDhmrcXvDsMf6qZwDMWwLmfdGXc6LQh7zsPj/rZ2vimEw2KAToMqRsoOryV0doaVDkrT5Xs36bnve5PEKuMHf2/LsRyPWb9aUt2suO4/Ouk8cc4hsMxEuh70pjiTFAmZeri9H6tzr0oFgUdl1LzPdvvFIg8VafLqA8Lw4ZOUmJAffRSts+4FKNYd5AAmMx0//hPbw4JVcKDCy2y1DyboFRoOhbvfC+NROXS9TjzG/BxV30fVOYCzZ4uRWefTKnrvZlYlDOeUfd30DT5r4BlRb5zqIgy6mZWXCBh7ELb/rqOXW6yKy56wXegu2mp8HN2NJF4htgw4qdoaOGEF62ljx/e2eivGafvTjSpSVH73bcXUt7CRWgZVcCjvoV/rPMh95B4e33+dv+9nSJGVOQnE7zpW4WhVJTjQ1bx8UG3YV/ozlwPwgvRSDy/LofT2WzvtY7eJwkz1ZiFMoHW3YfWu56yZowl0GaJSSuwrOgfHnY3uZbgzQOsM2r9xRu1O+u2D972UIM1PkCn+aDlkQsvxZNAr4wsFNdlP5+Aaa+pTTAXujV78KDmLAG+9+eNhPoQQhNkIfedbivpfafNjPIw6nC3LrbeNE3t4f0BrPuLTXy6BZjw2HNy5hAzhwgRCFcBhI9gyWM98EYBNIb4K34a3t73fzqPBgtTNVaST7oEPZjoosdW0cVvMFlCXjLsA8qYGQeAwXLwh1Fb/Ct89dCdpE4wTQN8nD7BRm4DvMGJjomyPkG/hGvGzDbP1MIe4BYAhq8muaHFy2XZbzXdwkWvGkG7yJ9UFHgI1betHB9wcrFPOCmxgzcRoWvRZ5F3qCv++fLnaZ412IbFmcBQ7UT6z4uS/jFlRIXBfwZ9w6YDUN8LaQC8rp0K0/fp19J3WAnYJgnuz3ZlIG8RvThI17Xo/aGkhL9DbW0re52fNEX73POQ7fi8tDxZza5padPxTatoS1haufpWeFh0aNwuVyHr2/G52MG09AtPvLyeLPDRB8yaLF9pxeAPSEnQV4L4RKviuu7dE/384g6QmBjvScefaSMpuCRPumx8RW0PC3MG7p85/F0KT7bMckDB0JGYHvdNtWulh3WfBKvei7xOnhjvp9YXT4ujanT4YZxgoCDfmJggW/prElCm/TRc7JPYkdyeC7d0s1fTgZcsoGZYNDFsUHzxojcd+ZFnKz2WG0hPboxSqvujVc3nVPIjJQRC1SohQXyvp2MBuHf4goPvxXaRR6bE7rkbitvu1WnWSr/ZYtOuPlngyYg2hOyrWFeQ7/JTFIHiu4b2m6qDwfoa9dZnzMYddctfPWlCgtWxTww6Odd7DqxzKA5VyhEKVLy3aniZMpYJSC7ccaM2Ot29/33Ia9hxyS8vyJwSZ73UIvG8uZxPqYQ/TdW3wW7YbuAEdMWEfPpOB2Y14ljuZ+hyg68ZZn07e1qfZd/OUinw2eDCvFDlfLN3SCtTEWwoDl3F+br8QhVHA8aa2BxYGsMzEsehCxYYJntGuo9Yb1qquBEc+5+upzHKNo/Nd5mg3yFfc0C2Z7QXtK2F1edfS94rbgv6DtePrCiTxVmlAqK/6NBUw7+vTvoYOiCL34bymtawjtuzsro4vxaddgNIaITgZsvDCoOpBWcW8D2U5kTG8Tdb/p0sKw4A3OX76yMj55M3AMVRY3a6oXa0GXgdK/k7X68MWc8YwLfTyfBkusYG1KENnLxEUrWQrsh3BDRYvVJ0fnFffVZvXygaMwmjQLFgdws8DXATnqnji8ltLK6k7zCnJczLiqfP6sr51jom32K0IYRvA1kAxIVW8Qk78acuj5tlJqIscDlkSKUrx6JL9+yFHa8F+1iMlyis7zFL/6rOVGYjIkFYm/WLdRjMrajTe3AjSdeMmllQzbpLweGdNZHKZBp9Qi7CORanDZJ+053XLiLT/KCvj2lV2yGYvjrO9hEBpV7AqhrlY0XbU0QuohLOVhcuB6wjMfJor6rzwiEX/jzX+Hmoqa4DUNhEwq+fGFo4tR5/zetdHRkOwzdehiWgSU/knzRjbZxypjDq/Pj5r6fPbU4TgKp9r25bO5vTVeLTSjcmm6TXPV0pRfz6vnQvHngtrV9X3pXfU5AR+EHd+e27gDCiF5K4RJknaPjtvqtD/iLfMNeLhnIOaPJjSmDp6HURdFUBIMYCocK2Vbj/4zwYbV8vTtPLcluDrQFcOB0OPi347SUs59/lk77d12UIZbC8Jub8gh8W6LCEMg42YbjyQQrTLbJQ/gP+frkyIZxmnZnNeVJ/L56tMzs5O3mkZiWHDRH8bJy/dlS/Noyyx0zXMTj4IPaWjt1du5oJTqwcXsAM58KHSe8s12in/Fr/wRaapgtd2zTgfomConix3ru9dbrjupR7/bybryz/hJwYofZYBMtGa6eL+xddkXu3AjJcGdJp5U4nTO7AHNMv3q6/ZmQh9VIJLIZ73FYxkf7GljtVLt51K7llBdxoviX8tolvLs4fDJf8tDkPCpwxXhIEypwf//wXPAU3Yw7todMGNXoCJWvYUkKY45MsuVcBFadh6ytNpu9NiYLLdNFsYlTtf8lPAHmNHuzw+mLMeSvoyis1lLa67Ak1duuvW4URdfAas0jVqKHEv5aT53UrcsWP47x8CdcLdl5BL4e3hfGTgnLth6udC1RVeHVlC5mS9f58hsHmvDqaklzs5W3X7FJ7MF2B+K0KQ08GWwoTp8eX/hGc14pqWyWcLV909s/WKFnx8RxeI5UPpB2E2Oq5clTGc6D8or8XHA6gMvm8U5A9iQ+zHCrmfPtmUt5tckFFCfvT5NT5AdsIKtkPqiGc17aXT4HblMjzObZ7R/Kt201ZfJqzbPvEMmDeBQQpIwbxJSd4mAC2/1LBCo2P23gdbjcmio4MnhmDRExfrgPwdWp9yab/WnoGM/O60BlOJDnYuf9SJiKhWtKKp8uZui4TBXmWdQYf1jUmSXE2qZryPYQ4+2H7GUZawlaqkacRihuukrpaEjwZL002q/7EszTc9UfbzeuJq73oygPKxfXdRm7A4H5CeGa4yYJhVcmHj2jbrmDh37fTY93Dp3XgTUP74jyFS447qXYTI2XBm+5d1dsH91M3L2hz+OZjFd81tOM2d2IYHNRhRvWsyNx8J7P3lbzZEmCW8rAmXw1gscLJSKriZjBU1rDltK1adKrnuXtE88EZBWEfUb0oMxvEnUKr0CbfpwasBUTI9g5x+3LpZt57zcNBJ57Zxwdp59JPkGSgCvbhVI/VGFD2eBUkNBxauMy1KmxmOoznG+EX1gRy70r74e7nD1DAbViG7NtTP0AN4upTmWEjiuz+YZ8oSHou7tCHYiipvugDtQcecouXvoEikTBznEcPoV7jSvUeOX97IGz8yDhjLO1gn6CSPEuOB5xrSlPZwtwXaE2e42XnwxU6CnRWmPS9sV5z1qR1rf5gvOF3AWsofEkr8WBaShUeak+WUOxANAFHHuAT90CXuzipX7yDGCYZudkQSpULhBTLT3eW3qTm68QK73g+QUfD/Kl9FuhBgxjdvazIU08Leamhb7tvQtq+/cIQJelLnJ5dJAOdSY/efxInXhX+fLEk8dyvWJNbeW29IIR6QuC3CXLf4R8+v7o8n0Cw0W8aaVSHVrbkSNt8QFBxx/L4jiEBppuSVBnpAFLny8fPM7RacfGB0WqJIQJx8Bh6SkuDA3VE+J1fXvs9ivj8IYfFnT12fqrswFtg6jnBic8xIHzhFm+FScvd0/FBfCmcLl04T7lG3uMKTLp15xeq3mpIZvajJZxszQdZESeEbzI71oNXJnEK5DvOkM9XjktNMjBvgN3I5C1Pr0J00TMnkZK9r2eaTmUUmec8BoVOs0a8cjXF3uGT7uOmBA0+S1dQpY0/w7/CC/xO0uX7AP9vFJpPuXhy3XbuZ+bjd7bvNsLCLvlasuN8/PWdQnxWPc2RSRxSjUEVKV1weDNmteO8+88ERm8fKjbbG+S1zQHJ8dyiDRhB3L2LCwk1sj+7cfcm707nETs1wqkbPDQ8W1CuBgyzr3o1i0Xu82L0O25eOkyN/vyxDScrFF2wxKo/wLCePXkvBHxiQAAAABJRU5ErkJggtgYWFukaGRpZ2VzdElEBGZyYW5kb21Q5levHsGtTQiVO30tX/fSmnFlbGVtZW50SWRlbnRpZmllcmpiaXJ0aF9kYXRlbGVsZW1lbnRWYWx1ZdkD7GoxOTkwLTA0LTMw2BhYW6RoZGlnZXN0SUQRZnJhbmRvbVD6Azd6j0l9GKIjk7ytxQuvcWVsZW1lbnRJZGVudGlmaWVyamdpdmVuX25hbWVsZWxlbWVudFZhbHVlbUNvbm5vciBKdXN0aW7YGFhbpGhkaWdlc3RJRANmcmFuZG9tUOcAAr7rsAun6N013/32921xZWxlbWVudElkZW50aWZpZXJqaXNzdWVfZGF0ZWxlbGVtZW50VmFsdWXZA+xqMjAyNC0wMS0wMdgYWFOkaGRpZ2VzdElECmZyYW5kb21Q73TwdasU4qKtNarutPNAJ3FlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl8xOGxlbGVtZW50VmFsdWVkdHJ1ZdgYWFOkaGRpZ2VzdElEBWZyYW5kb21Qwa9hWf6bIkpRHSGyEvdqQ3FlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl8yMWxlbGVtZW50VmFsdWVkdHJ1ZdgYWFykaGRpZ2VzdElEDWZyYW5kb21Q+UqiYYLdKafjOiK3u7CcG3FlbGVtZW50SWRlbnRpZmllcmtleHBpcnlfZGF0ZWxlbGVtZW50VmFsdWXZA+xqMjAzNS0xMi0zMNgYWFakaGRpZ2VzdElEDmZyYW5kb21Q5NLYphEy1GcFbe0Bd4PKknFlbGVtZW50SWRlbnRpZmllcmtmYW1pbHlfbmFtZWxlbGVtZW50VmFsdWVnSmFja21hbtgYWFikaGRpZ2VzdElEAGZyYW5kb21Q9dioUYwBh5JD/6OsXWvi1XFlbGVtZW50SWRlbnRpZmllcm1yZXNpZGVudF9jaXR5bGVsZW1lbnRWYWx1ZWdDYXBpdG9s2BhYW6RoZGlnZXN0SUQJZnJhbmRvbVDUK7BT8/P3xDW0qzC0pNVFcWVsZW1lbnRJZGVudGlmaWVybnJlc2lkZW50X3N0YXRlbGVsZW1lbnRWYWx1ZWlNb250Y2xpZmbYGFhbpGhkaWdlc3RJRAJmcmFuZG9tUBEkyO7ctwliViRxeEu3PlBxZWxlbWVudElkZW50aWZpZXJvZG9jdW1lbnRfbnVtYmVybGVsZW1lbnRWYWx1ZWhETDI0NTE5ONgYWFWkaGRpZ2VzdElECGZyYW5kb21Q3QF6v9KTgB+ZvPPb217X/nFlbGVtZW50SWRlbnRpZmllcm9pc3N1aW5nX2NvdW50cnlsZWxlbWVudFZhbHVlYkFV2BhYZKRoZGlnZXN0SUQMZnJhbmRvbVCJW7IyUANh442SHJKctLZCcWVsZW1lbnRJZGVudGlmaWVycHJlc2lkZW50X2FkZHJlc3NsZWxlbWVudFZhbHVlcDI0M0IgTWFpbiBTdHJlZXTYGFhWpGhkaWdlc3RJRAZmcmFuZG9tUPkJBaRKM6L+6swQJij7Ms1xZWxlbWVudElkZW50aWZpZXJwcmVzaWRlbnRfY291bnRyeWxlbGVtZW50VmFsdWViQVXYGFhipGhkaWdlc3RJRBBmcmFuZG9tUKOql384E7x7FwRS6kOWlEBxZWxlbWVudElkZW50aWZpZXJxaXNzdWluZ19hdXRob3JpdHlsZWxlbWVudFZhbHVlbU1vbnRjbGlmZiBETVbYGFjFpGhkaWdlc3RJRAtmcmFuZG9tUCYYfim7HoK2GzTGVky5z9xxZWxlbWVudElkZW50aWZpZXJzZHJpdmluZ19wcml2aWxlZGdlc2xlbGVtZW50VmFsdWV4bVsgICB7ICAgICAidmVoaWNsZV9jYXRlZ29yeV9jb2RlIjogIkIiLCAgICAgImlzc3VlX2RhdGUiOiAiMjAyMi0xMC0xMCIsICAgICAiZXhwaXJ5X2RhdGUiOiAiMjAzMi0xMC0xMCIgICB9IF3YGFhcpGhkaWdlc3RJRAdmcmFuZG9tUI8d/KvZyT02F8EQc8yrL3ZxZWxlbWVudElkZW50aWZpZXJ2dW5fZGlzdGluZ3Vpc2hpbmdfc2lnbmxlbGVtZW50VmFsdWViQVU=",
"decoded": {
"namespaces": {
"org.iso.18013.5.1": [
{
"digestID": 1,
"random": "XYGwWHUk5ugOnrIrivXZdQ==",
"elementIdentifier": "sex",
"elementValue": {
"type": "string",
"value": "1"
}
},
{
"digestID": 15,
"random": "VHu6WoNegd/NwwUmKLfU6g==",
"elementIdentifier": "portrait",
"elementValue": {
"type": "binary",
"value": "iVBORw0KGgoAAAANSUhEUgAAAG8AAACPCAMAAADDX3XQAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAJcEhZcwAACxMAAAsTAQCanBgAAABIUExURa+uqbmMda6Ca6J2YAwIBhwTD5ZsVqqqpaOinpyblpSSjtGlkT0rIi0fGIpiTX1XQ8eZgkw3LF5CM4qJhW5LOdeyoHx6dWRhXLXA62kAABHKSURBVGjejJqJYqMgEIZRo6gBDWjl/d905+JSY5butk1zfPxzMShq1PyF/05DwRd9U0rxT03/LkNf/8Dvvw4VeYgbK1ikRUYBLqD0V52p8lhnpirJKqkT2KjT1C7vle8nQqlR6/hsxKhK5qiENRawwpwsUalar1IRm5C65GqVKGekYkZGXlmqfHWWXkm7PIyGKbmV/7SuFKpo++j+0gsqRlFlU32ewI370q/jOT7FiHe4+PzX8CSgLt9YTDXrGy/5kHTEd2Tr1HYsBGd5OZpTdhWTrhNCFVa7ZtJFUvpjHT86OzNOmDN9vFOW8k5JLqjq2VOCq1wiToFaEm9rQJHnMfhj9snUTrwcXjkDTsGaHHLjtTpAU2iDm2ngD53sWGfMOZZypKrbGqeSf4oMopfOwTtjXNjmbZunMZeIomIWs72WniuvdIH8i3E1HT4E54ztTZjnI8A4JhI7nQWqipaLvTonlUrCsjjx2XYcWzB9/+phWGfoF+sPFDpeLHpKfl0F8XUFKrUhbJy24AFBA4H8ncggclbk1zNN361aF15RMwU2gvkc6okQAiLZGmPNsW2UUNN2zIU1q/qtdFXkLgmebYmwYF9xsCjjDCFBnrMmHNs0B2ddCP4YY7yeq3hptVP1yo5DQzpbsUBT3zvvjbVIhkcAcvER0qebBqCuRKqu+MnM44SGfJG/ItQavxvr9tWjTGtkEhaH+DTMX1nZg+q0yCRc/yoGfrY1+w7UdXXOedvzX4EN6vreOPzl+ClQXXIUciBQTCYgWg8GGNOBOT1IBIEpchAEEoEMiXo86JP2R9cOhgQPRIppgEaDz1qXdV934yw4cXfWplAFtUSE4Y4wPehTRdoldVOQT4qGlAe9W4djWcl2BpJSosmSQiw/YNzezUE/+PC6xCAukpLvKCLgp9kHAGI+osyk37AP6VVh+xqlRd+XxxTYUJLdFCboPpSAaQcmxTwBSJ8jCZ71otBuUuXueOqcDYAzlSj8xwkGPAABEaLTEiXLdzEtIbL8ph54uhaoj4TrMck4tSxLRRnoIm9MH4G9GJRzgkpQGB9ysPQe/La5PiWbhQ+m8lGEDX0oRKvlwg3RklKT7YnzOZ7yr+rqRpddwsMaU6W95eItyd4nnviP/ujmH/5Ljzcb1RmaLwTFifei6GRtEKUvK9XbO66kNNyknnIiWXMMMSaxlHhc0YFnKh6YjqeEIObhy/fdif9obRy/5Xslb4qTJ56TkmHsyaLJnzidnuXt9Oq4SNrwEDGZfaT123Cs9MTjmafgj+kZeajHGxMDhgXPP+0JuWd4TYjpRjXYYmdmexvdlpb4nl+Gf5VSbvvUCRj/k6ZVSCWYSyJVFptqFT2KQP5U6zg9MT59jhcUuP206BjrtI3ZEJMiDYnB1F0YI0XIyBIhCl/Av18eigej65Od6N1G6iYWR48li6MixoSUadYuq6BYG1IibOrHmE1MPSMfYwjid7/DWNd1WSDseW3F1+EkqFAb5knJphiwx/YrQKc+V3sn6/nOY12GYei6dhiW3bsEIMl7BkZ9+P1p3WXe9iqSjywpsoDWtU3TfD5N0wFyBQwpo+fha8dYsZSyKW3M+FRjcMTsiw0CdBBgwWVBYcDi0TQtIpcFuguayYCPVnJsDBgOulk9NhaY7bGTJhc6tGIHrLZtCPemQci2hSfIxDxQc2lQnHd4tqbSIU2Nctwv5LKWLRlxkQlUlNryKIGx7H3vY6TnjPsSWc5WnD/jIu/v/QeDiR+GCg/ErlSI8nIZxmeBkzlsbm1ZXlvx/viLicwklSRwGHZXGtSGH/F5uM0UDdDO8ppCHqn7SxLfhUTkYaqYXNePH7wQKh6ac2hr9/3VxLcIbBLP58X5N+/YXLE1WTnDb+WxYUVh4g1rwYO99y97HpNPk0v6iNfE8Cx4Ud8n61txSYof4X7w9BymUHSUO5WwWt870gRX6yNe+gR3PGYfru7zceax9y4GfeesSPoW4MVNE37Cw/og1yvG7Sj3lt/1FeESA4Zq3F7w7DH+qmcAzFsC5n3Rl3Oi0Ie87D4/62dr4phMNigE6DKkbKDq8ldHaGlQ5K0+V7N+m573uTxCrjB39vy7Ecj1m/WlLdrLjuPzrpPHHOIbDMRLoe9KY4kxQJmXq4vR+rc69KBYFHZdS8z3b7xSIPFWny6gPC8OGTlJiQH30UrbPuBSjWHeQAJjMdP/4T28OCVXCgwststQ8m6BUaDoW73wvjUTl0vU48xvwcVd9H1TmAs2eLkVnn0yp672ZWJQznlH3d9A0+a+AZUW+c6iIMupmVlwgYexC2/66jl1usisuesF3oLtpqfBzdjSReIbYMOKnaGjhhBetpY8f3tnorxmn7040qUlR+923F1LewkVoGVXAo76Ff6zzIfeQeHt9/nb/vZ0iRlTkJxO86VuFoVSU40NW8fFBt2Ff6M5cD8IL0Ug8vy6H09ls77WO3icJM9WYhTKB1t2H1ruesmaMJdBmiUkrsKzoHx52N7mW4M0DrDNq/cUbtTvrtg/e9lCDNT5Ap/mg5ZELL8WTQK+MLBTXZT+fgGmvqU0wF7o1e/Cg5iwBvvfnjYT6EEITZCH3nW4r6X2nzYzyMOpwty623jRN7eH9Aaz7i018ugWY8NhzcuYQM4cIEQhXAYSPYMljPfBGATSG+Ct+Gt7e9386jwYLUzVWkk+6BD2Y6KLHVtHFbzBZQl4y7APKmBkHgMFy8IdRW/wrfPXQnaROME0DfJw+wUZuA7zBiY6Jsj5Bv4Rrxsw2z9TCHuAWAIavJrmhxctl2W813cJFrxpBu8ifVBR4CNW3rRwfcHKxTzgpsYM3EaFr0WeRd6gr/vny52meNdiGxZnAUO1E+s+Lkv4xZUSFwX8GfcOmA1DfC2kAvK6dCtP36dfSd1gJ2CYJ7s92ZSBvEb04SNe16P2hpIS/Q21tK3udnzRF+9zzkO34vLQ8Wc2uaWnT8U2raEtYWrn6VnhYdGjcLlch69vxudjBtPQLT7y8nizw0QfMmixfacXgD0hJ0FeC+ESr4rru3RP9/OIOkJgY70nHn2kjKbgkT7psfEVtDwtzBu6fOfxdCk+2zHJAwdCRmB73TbVrpYd1nwSr3ou8Tp4Y76fWF0+Lo2p0+GGcYKAg35iYIFv6axJQpv00XOyT2JHcngu3dLNX04GXLKBmWDQxbFB88aI3HfmRZys9lhtIT26MUqr7o1XN51TyIyUEQtUqIUF8r6djAbh3+IKD78V2kUemxO65G4rb7tVp1kq/2WLTrj5Z4MmINoTsq1hXkO/yUxSB4ruG9puqg8H6GvXWZ8zGHXXLXz1pQoLVsU8MOjnXew6scygOVcoRClS8t2p4mTKWCUgu3HGjNjrdvf99yGvYcckvL8icEme91CLxvLmcT6mEP03Vt8Fu2G7gBHTFhHz6TgdmNeJY7mfocoOvGWZ9O3tan2XfzlIp8NngwrxQ5Xyzd0grUxFsKA5dxfm6/EIVRwPGmtgcWBrDMxLHoQsWGCZ7RrqPWG9aqrgRHPufrqcxyjaPzXeZoN8hX3NAtme0F7SthdXnX0veK24L+g7Xj6wok8VZpQKiv+jQVMO/r076GDogi9+G8prWsI7bs7K6OL8WnXYDSGiE4GbLwwqDqQVnFvA9lOZExvE3W/6dLCsOANzl++sjI+eTNwDFUWN2uqF2tBl4HSv5O1+vDFnPGMC308nwZLrGBtShDZy8RFK1kK7IdwQ0WL1SdH5xX31Wb18oGjMJo0CxYHcLPA1wE56p44vJbSyupO8wpyXMy4qnz+rK+dY6Jt9itCGEbwNZAMSFVvEJO/GnLo+bZSaiLHA5ZEilK8eiS/fshR2vBftYjJcorO8xS/+qzlRmIyJBWJv1i3UYzK2o03twI0nXjJpZUM26S8HhnTWRymQafUIuwjkWpw2SftOd1y4i0/ygr49pVdshmL46zvYRAaVewKoa5WNF21NELqISzlYXLgesIzHyaK+q88IhF/481/h5qKmuA1DYRMKvnxhaOLUef83rXR0ZDsM3XoYloElP5J80Y22ccqYw6vz4+a+nz21OE4Cqfa9uWzub01Xi00o3Jpuk1z1dKUX8+r50Lx54La1fV96V31OQEfhB3fntu4AwoheSuESZJ2j47b6rQ/4i3zDXi4ZyDmjyY0pg6eh1EXRVASDGAqHCtlW4/+M8GG1fL07Ty3Jbg60BXDgdDj4t+O0lLOff5ZO+3ddlCGWwvCbm/IIfFuiwhDIONmG48kEK0y2yUP4D/n65MiGcZp2ZzXlSfy+erTM7OTt5pGYlhw0R/Gycv3ZUvzaMssdM1zE4+CD2lo7dXbuaCU6sHF7ADOfCh0nvLNdop/xa/8EWmqYLXds04H6JgqJ4sd67vXW647qUe/28m68s/4ScGKH2WATLRmuni/sXXZF7twIyXBnSaeVOJ0zuwBzTL96uv2ZkIfVSCSyGe9xWMZH+xpY7VS7edSu5ZQXcaL4l/LaJby7OHwyX/LQ5DwqcMV4SBMqcH//8FzwFN2MO7aHTBjV6AiVr2FJCmOOTLLlXARWnYesrTabvTYmCy3TRbGJU7X/JTwB5jR7s8PpizHkr6MorNZS2uuwJNXbrr1uFEXXwGrNI1aihxL+Wk+d1K3LFj+O8fAnXC3ZeQS+Ht4Xxk4Jy7YernQtUVXh1ZQuZkvX+fIbB5rw6mpJc7OVt1+xSezBdgfitCkNPBlsKE6fHl/4RnNeKalslnC1fdPbP1ihZ8fEcXiOVD6QdhNjquXJUxnOg/KK/FxwOoDL5vFOQPYkPsxwq5nz7ZlLebXJBRQn70+TU+QHbCCrZD6ohnNe2l0+B25TI8zm2e0fyrdtNWXyas2z7xDJg3gUEKSMG8SUneJgAtv9SwQqNj9t4HW43JoqODJ4Zg0RMX64D8HVqfcmm/1p6BjPzutAZTiQ52Ln/UiYioVrSiqfLmbouEwV5lnUGH9Y1JklxNqma8j2EOPth+xlGWsJWqpGnEYobrpK6WhI8GS9NNqv+xLM03PVH283riau96MoDysX13UZuwOB+QnhmuMmCYVXJh49o265g4d+302Pdw6d14E1D++I8hUuOO6l2EyNlwZvuXdXbB/dTNy9oc/jmYxXfNbTjNndiGBzUYUb1rMjcfCez95W82RJglvKwJl8NYLHCyUiq4mYwVNaw5bStWnSq57l7RPPBGQVhH1G9KDMbxJ1Cq9Am36cGrAVEyPYOcfty6Wbee83DQSee2ccHaefST5BkoAr24VSP1RhQ9ngVJDQcWrjMtSpsZjqM5xvhF9YEcu9K++Hu5w9QwG1YhuzbUz9ADeLqU5lhI4rs/mGfKEh6Lu7Qh2Ioqb7oA7UHHnKLl76BIpEwc5xHD6Fe40r1Hjl/eyBs/Mg4YyztYJ+gkjxLjgeca0pT2cLcF2hNnuNl58MVOgp0Vpj0vbFec9akda3+YLzhdwFrKHxJK/FgWkoVHmpPllDsQDQBRx7gE/dAl7s4qV+8gxgmGbnZEEqVC4QUy093lt6k5uvECu94PkFHw/ypfRboQYMY3b2syFNPC3mpoW+7b0Lavv3CECXpS5yeXSQDnUmP3n8SJ14V/nyxJPHcr1iTW3ltvSCEekLgtwly3+EfPr+6PJ9AsNFvGmlUh1a25EjbfEBQccfy+I4hAaabklQZ6QBS58vHzzO0WnHxgdFqiSECcfAYekpLgwN1RPidX177PYr4/CGHxZ09dn6q7MBbYOo5wYnPMSB84RZvhUnL3dPxQXwpnC5dOE+5Rt7jCky6decXqt5qSGb2oyWcbM0HWREnhG8yO9aDVyZxCuQ7zpDPV45LTTIwb4DdyOQtT69CdNEzJ5GSva9nmk5lFJnnPAaFTrNGvHI1xd7hk+7jpgQNPktXUKWNP8O/wgv8TtLl+wD/bxSaT7l4ct127mfm43e27zbCwi75WrLjfPz1nUJ8Vj3NkUkcUo1BFSldcHgzZrXjvPvPBEZvHyo22xvktc0ByfHcog0YQdy9iwsJNbI/u3H3Ju9O5xE7NcKpGzw0PFtQrgYMs696NYtF7vNi9DtuXjpMjf78sQ0nKxRdsMSqP8Cwnj15LwR8YkAAAAASUVORK5CYII="
}
},
{
"digestID": 4,
"random": "5levHsGtTQiVO30tX/fSmg==",
"elementIdentifier": "birth_date",
"elementValue": {
"type": "date",
"value": "1990-04-30"
}
},
{
"digestID": 17,
"random": "+gM3eo9JfRiiI5O8rcULrw==",
"elementIdentifier": "given_name",
"elementValue": {
"type": "string",
"value": "Connor Justin"
}
},
{
"digestID": 3,
"random": "5wACvuuwC6fo3TXf/fb3bQ==",
"elementIdentifier": "issue_date",
"elementValue": {
"type": "date",
"value": "2024-01-01"
}
},
{
"digestID": 10,
"random": "73TwdasU4qKtNarutPNAJw==",
"elementIdentifier": "age_over_18",
"elementValue": {
"type": "string",
"value": "true"
}
},
{
"digestID": 5,
"random": "wa9hWf6bIkpRHSGyEvdqQw==",
"elementIdentifier": "age_over_21",
"elementValue": {
"type": "string",
"value": "true"
}
},
{
"digestID": 13,
"random": "+UqiYYLdKafjOiK3u7CcGw==",
"elementIdentifier": "expiry_date",
"elementValue": {
"type": "date",
"value": "2035-12-30"
}
},
{
"digestID": 14,
"random": "5NLYphEy1GcFbe0Bd4PKkg==",
"elementIdentifier": "family_name",
"elementValue": {
"type": "string",
"value": "Jackman"
}
},
{
"digestID": 0,
"random": "9dioUYwBh5JD/6OsXWvi1Q==",
"elementIdentifier": "resident_city",
"elementValue": {
"type": "string",
"value": "Capitol"
}
},
{
"digestID": 9,
"random": "1CuwU/Pz98Q1tKswtKTVRQ==",
"elementIdentifier": "resident_state",
"elementValue": {
"type": "string",
"value": "Montcliff"
}
},
{
"digestID": 2,
"random": "ESTI7ty3CWJWJHF4S7c+UA==",
"elementIdentifier": "document_number",
"elementValue": {
"type": "string",
"value": "DL245198"
}
},
{
"digestID": 8,
"random": "3QF6v9KTgB+ZvPPb217X/g==",
"elementIdentifier": "issuing_country",
"elementValue": {
"type": "string",
"value": "AU"
}
},
{
"digestID": 12,
"random": "iVuyMlADYeONkhySnLS2Qg==",
"elementIdentifier": "resident_address",
"elementValue": {
"type": "string",
"value": "243B Main Street"
}
},
{
"digestID": 6,
"random": "+QkFpEozov7qzBAmKPsyzQ==",
"elementIdentifier": "resident_country",
"elementValue": {
"type": "string",
"value": "AU"
}
},
{
"digestID": 16,
"random": "o6qXfzgTvHsXBFLqQ5aUQA==",
"elementIdentifier": "issuing_authority",
"elementValue": {
"type": "string",
"value": "Montcliff DMV"
}
},
{
"digestID": 11,
"random": "Jhh+KbsegrYbNMZWTLnP3A==",
"elementIdentifier": "driving_priviledges",
"elementValue": {
"type": "string",
"value": "[ { \"vehicle_category_code\": \"B\", \"issue_date\": \"2022-10-10\", \"expiry_date\": \"2032-10-10\" } ]"
}
},
{
"digestID": 7,
"random": "jx38q9nJPTYXwRBzzKsvdg==",
"elementIdentifier": "un_distinguishing_sign",
"elementValue": {
"type": "string",
"value": "AU"
}
}
]
},
"issuerAuth": "hEOhASahGCFZApcwggKTMIICOKADAgECAgpSc4u6ewLWeF9jMAoGCCqGSM49BAMCMC4xCzAJBgNVBAYTAkFVMR8wHQYDVQQDDBZsYWJzLm1hdHRybGFicy5jbyBJQUNBMB4XDTI0MDcyOTEwMTMwMVoXDTI1MTAyNzEwMTMwMVowOTELMAkGA1UEBhMCQVUxKjAoBgNVBAMMIWxhYnMubWF0dHJsYWJzLmNvIERvY3VtZW50IFNpZ25lcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIwfV3GMKmD2/AeX0nyM17WWpDcu0ow/l+LwX7JsrvMmGV2IJmkJnCLPlFJS8OEZCH2Awx6dcHZM9ypPWPxRFJ+jggExMIIBLTAdBgNVHQ4EFgQUeDSAUoc4IOG98TfH4oBwEAXzjCEwHwYDVR0jBBgwFoAUjpQIFBA5F7EMBwhiw7HRjumaE/4wDgYDVR0PAQH/BAQDAgeAMCQGA1UdEQQdMBuGGWh0dHBzOi8vbGFicy5tYXR0cmxhYnMuY28wJAYDVR0SBB0wG4YZaHR0cHM6Ly9sYWJzLm1hdHRybGFicy5jbzB4BgNVHR8EcTBvMG2ga6BphmdodHRwczovL2xhYnMudmlpLmF1MDEubWF0dHIuZ2xvYmFsL3YyL2NyZWRlbnRpYWxzL21vYmlsZS9pYWNhcy9mNDU5ZjliMi0xNzZkLTQ5NzAtYjRjYi0xZGFhOGVmOGE0ZTUvY3JsMBUGA1UdJQEB/wQLMAkGByiBjF0FAQIwCgYIKoZIzj0EAwIDSQAwRgIhAKKrbWcL0zSR7v+eMVnhvGOPfYN5Z1N7Z3bwRmfC9UTuAiEA3VS+f1ejLdHkyBKnzUjPBjOHYZ0uFX1vMS/QShgy0mNZ5MXYGFnkwKdndmVyc2lvbmMxLjBvZGlnZXN0QWxnb3JpdGhtZ1NIQS0yNTZsdmFsdWVEaWdlc3RzoXFvcmcuaXNvLjE4MDEzLjUuMbIAWCBHKXMYF97lb0Z04rfrV2P4jXk9xglx/6jKG6j2DciXxgFYIDs6fkgoiSVVbePNCUWpJI6L+rOFssU9ezcU4yNO05wnAlggo4SzZYZ40QSCJ78EpUzn96E+7t4StK9QjHI8hO2AHwMDWCBsMR9aWU8u548e7/ms/o2YTF0e5huk7SE7zaKrI/fDlARYIKHbUNrLFOo0fZ9tp0aWR7R9ivZydJzSiCMuTGFRqp5KBVggtGdZGpki+cVQ7gcwMPso1YPt7zMNrVOSPUxJjaszz7EGWCB1D6PoCZyVXGTsPIz0Vkm3PZisEJB+RerimlCCE8prcAdYILf8HxOxwVwds6KAwXCNta9aYHeqFc+7A8kLgItGmQhXCFggCAA4gbM54lbSlP0CCQ+0Xxzhqhi7lyqMdUNpQG1iOvoJWCBlKg/lHR7Q/tiC0GffuQ65zGcP6gdjvxx6pgUytNoRAgpYIJIrLr3TethAOk2L+H1niDFeh4n9S+lGsQ2TQ2W+L9mHC1gg4ysAT7MLuJGIjfa0aWmKlRvwegYldoRMrWPBa1oZp1UMWCBejb71fRUoVBycI/hb6+d9li8Nt/AGDr4cHqeFgqBNKg1YIEEWtB5rzkDXMcIL589lMjoSke6XEUextEsTR0FjpGfsDlggqhJ91EIJZrTW3ovq5Fc5yX3hl+24F0s2PM+kUXY4y0EPWCBwj8knItIZzL0BOLINHgHAIfh5pkXlftuhcEnFcxY9KxBYINfHE/iNIeoMvp5KXeNBZlfHhHIlEfvjGbsaAx1IwJN3EVggm43zZkO72fc1lwnX/8Q4O1C/BV2CMQBRlf11ZwgIp5RtZGV2aWNlS2V5SW5mb6FpZGV2aWNlS2V5pAECIAEhWCCIwikMm+n28zOyVBGiAr/UOuX81Zgv7i3PzdvwPzG5biJYIDj/fzDqOGd/zfMfj0+xRiMUMX0+Y2VIOVmS66zFm1XxZ2RvY1R5cGV2b3JnLmlzby4xODAxMy41LjEubURMCWx2YWxpZGl0eUluZm+janZhbGlkVW50aWzAdDIwMjQtMTAtMDJUMjI6Mjg6NDBaaXZhbGlkRnJvbcB0MjAyNC0wOS0wMlQyMjoyODo0MVpmc2lnbmVkwHQyMDI0LTA5LTAyVDIyOjI4OjQxWmlfYnJhbmRpbmemZG5hbWVuRHJpdmVyIExpY2Vuc2VrZGVzY3JpcHRpb254MERldGFpbHMgYWJvdXQgeW91ciBhZ2UsIGlkZW50aXR5ICYgbGljZW5zZSBjbGFzc29iYWNrZ3JvdW5kQ29sb3JnIzAwN0VBN253YXRlcm1hcmtJbWFnZaJmZm9ybWF0Y3N2Z2RkYXRhWS0FPHN2ZyB3aWR0aD0iMjQ1IiBoZWlnaHQ9IjE1MCIgdmlld0JveD0iMCAwIDI0NSAxNTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8xMjEzXzU1MjApIj4KPHBhdGggZD0iTTE1NS4wNjkgNjYuODY3NUwxNDAuNDgyIDU1LjA5NjlDMTQwLjA4MiA1NC43NzQ3IDEzOS44NDggNTQuMjg3NiAxMzkuODQ4IDUzLjc3MjdMMTM5Ljg0OCA0NC42NDc5QzEzOS44NDggNDMuOTg1OCAxNDAuMjM0IDQzLjM4MiAxNDAuODM1IDQzLjEwM0wxNTMuMDQ1IDM3LjQ0MDlDMTUzLjkyNyAzNy4wMzI1IDE1NC4yODggMzUuOTY3IDE1My44MzYgMzUuMTA0NUwxNDkuNzgyIDI3LjM4NTFDMTQ5LjY1MyAyNy4xNDE2IDE0OS41ODcgMjYuODcwMiAxNDkuNTg3IDI2LjU5MzdMMTQ5LjU4NyAyMi41NDVDMTQ5LjU4NyAyMS45NDM3IDE0OS45MDcgMjEuMzg1NyAxNTAuNDI0IDIxLjA3ODdMMTc1LjM3NiA2LjM1MjcyQzE3NS45ODcgNS45OTI1IDE3Ni43NTggNi4wNDgzMSAxNzcuMzExIDYuNDk0NzhMMjAwLjk2NyAyNS42MDE4TDIwMC45NjcgMzguNzA2OEMyMDAuOTY3IDM5LjY0NzkgMjAwLjIwNiA0MC40MDg5IDE5OS4yNjUgNDAuNDA4OUwxODUuODIgNDEuMjQ1M0MxODQuODc5IDQxLjI0NTMgMTg0LjExOCA0Mi4wMDYzIDE4NC4xMTggNDIuOTQ3NUwxODMuMDA5IDU1LjU5NDFDMTgzLjAwOSA1Ni41MzUyIDE4My43NyA1Ny4yOTYyIDE4NC43MTEgNTcuMjk2MkMxODUuNjUyIDU3LjI5NjIgMTg2LjQxMyA1OC4wNTczIDE4Ni40MTMgNTguOTk4NEwxODYuNDEzIDcwLjUyNTVDMTg2LjQxMyA3MS40NjY3IDE4NS42NTIgNzIuMjI3NyAxODQuNzExIDcyLjIyNzdMMTczLjE3NiA3Mi4yMjc3QzE3My4wMTIgNzIuMjI3NyAxNzIuODQ3IDcyLjIwMjMgMTcyLjY4NyA3Mi4xNTY3TDE1NS4wNjYgNjYuODY3NSIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTE5Mi44OCA1OC41MjIxTDE5Ni41OTYgNTQuMzMyNEMxOTYuNzY3IDU0LjEzOTYgMTk2Ljg0MyA1My44ODExIDE5Ni44MDUgNTMuNjI2M0wxOTUuNjYxIDQ2LjAxODhDMTk1LjYxIDQ1LjY3NjkgMTk1LjM2MSA0NS4zOTc5IDE5NS4wMjcgNDUuMzA4NUwxOTEuMjk5IDQ0LjMwOTdDMTkwLjgzNSA0NC4xODU0IDE5MC4zNTkgNDQuNDYwNiAxOTAuMjM0IDQ0LjkyNDNMMTg3Ljc4NCA1NC4wNjk2QzE4Ny42NiA1NC41MzMzIDE4Ny45MzUgNTUuMDEgMTg4LjM5OSA1NS4xMzQyTDE5MC4zODYgNTUuNjY2N0MxOTAuNzA0IDU1Ljc1MTkgMTkwLjk0NyA1Ni4wMDk1IDE5MS4wMTMgNTYuMzMyTDE5MS4zNzggNTguMTE5NkMxOTEuNTIyIDU4LjgyMTQgMTkyLjQwNSA1OS4wNTggMTkyLjg4IDU4LjUyMjFaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTI2LjAxNiA5Ni4wMDk3TDEyNi40NjggODIuMjYyNUgxMzAuNTM0TDEzMy4xMTYgOTEuMDA3N0gxMzMuMjU2TDEzNS44MjcgODIuMjYyNUgxMzkuODgyTDE0MC4zNDQgOTYuMDA5N0gxMzcuNjEyTDEzNy40ODMgOTEuMDYxNUwxMzcuMzY1IDg1LjM0OTdIMTM3LjE5M0wxMzQuNDkzIDk0LjQwNjlIMTMxLjg2OEwxMjkuMTU3IDg1LjM0OTdIMTI4Ljk4NUwxMjguODY3IDkxLjA3MjNMMTI4Ljc0OSA5Ni4wMDk3SDEyNi4wMTZaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTQ4LjM0NiA5Ni4zMDAxQzE0Ni43MTEgOTYuMzAwMSAxNDUuNDcgOTUuODgwNiAxNDQuNjI0IDk1LjA0MTVDMTQzLjc4NSA5NC4yMDI1IDE0My4zNjUgOTMuMDA4NSAxNDMuMzY1IDkxLjQ1OTVWOTAuMDI4OUMxNDMuMzY1IDg4LjQ3MjcgMTQzLjc4NSA4Ny4yNzUxIDE0NC42MjQgODYuNDM2MUMxNDUuNDcgODUuNTg5OSAxNDYuNzExIDg1LjE2NjggMTQ4LjM0NiA4NS4xNjY4QzE0OS45NzMgODUuMTY2OCAxNTEuMjA3IDg1LjU4OTkgMTUyLjA0NiA4Ni40MzYxQzE1Mi44ODUgODcuMjc1MSAxNTMuMzA1IDg4LjQ3MjcgMTUzLjMwNSA5MC4wMjg5VjkxLjQ1OTVDMTUzLjMwNSA5My4wMDg1IDE1Mi44ODUgOTQuMjAyNSAxNTIuMDQ2IDk1LjA0MTVDMTUxLjIxNCA5NS44ODA2IDE0OS45ODEgOTYuMzAwMSAxNDguMzQ2IDk2LjMwMDFaTTE0OC4zNDYgOTQuMDk1QzE0OS4wNjMgOTQuMDk1IDE0OS42MDggOTMuODc5OCAxNDkuOTgxIDkzLjQ0OTVDMTUwLjM2MSA5My4wMTkzIDE1MC41NTEgOTIuNDAyNSAxNTAuNTUxIDkxLjU5OTRWODkuODg5QzE1MC41NTEgODkuMDcxNSAxNTAuMzYxIDg4LjQ0NzYgMTQ5Ljk4MSA4OC4wMTc0QzE0OS42MDggODcuNTc5OSAxNDkuMDYzIDg3LjM2MTIgMTQ4LjM0NiA4Ny4zNjEyQzE0Ny42MjEgODcuMzYxMiAxNDcuMDY5IDg3LjU3OTkgMTQ2LjY4OSA4OC4wMTc0QzE0Ni4zMTYgODguNDQ3NiAxNDYuMTMgODkuMDcxNSAxNDYuMTMgODkuODg5VjkxLjU5OTRDMTQ2LjEzIDkyLjQwMjUgMTQ2LjMxNiA5My4wMTkzIDE0Ni42ODkgOTMuNDQ5NUMxNDcuMDY5IDkzLjg3OTggMTQ3LjYyMSA5NC4wOTUgMTQ4LjM0NiA5NC4wOTVaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTYzLjI2OCA5Ni4wMDk3Vjg5LjY3MzlDMTYzLjI2OCA4OS4yMjkzIDE2My4yMDcgODguODQ1NiAxNjMuMDg1IDg4LjUyMjlDMTYyLjk3IDg4LjIwMDIgMTYyLjc3NiA4Ny45NDkyIDE2Mi41MDQgODcuNzdDMTYyLjIzMSA4Ny41OTA3IDE2MS44NTkgODcuNTAxIDE2MS4zODUgODcuNTAxQzE2MC45NjkgODcuNTAxIDE2MC42MDQgODcuNTc2MyAxNjAuMjg4IDg3LjcyNjlDMTU5Ljk4IDg3Ljg3NzUgMTU5LjcyNSA4OC4wODE5IDE1OS41MjQgODguMzQwMUMxNTkuMzMxIDg4LjU5MTEgMTU5LjE4NCA4OC44Nzc5IDE1OS4wODMgODkuMjAwNkwxNTguNjUzIDg3LjY5NDdIMTU5LjE2OUMxNTkuMjg0IDg3LjIyODUgMTU5LjQ3NCA4Ni44MDkgMTU5LjczOSA4Ni40MzYxQzE2MC4wMTIgODYuMDYzMiAxNjAuMzc4IDg1Ljc2OTIgMTYwLjgzNyA4NS41NTQxQzE2MS4zMDMgODUuMzMxNyAxNjEuODg0IDg1LjIyMDYgMTYyLjU3OSA4NS4yMjA2QzE2My4zOSA4NS4yMjA2IDE2NC4wNDYgODUuMzc0OCAxNjQuNTQ4IDg1LjY4MzFDMTY1LjA1IDg1Ljk4NDMgMTY1LjQxOSA4Ni40MzYxIDE2NS42NTYgODcuMDM4NUMxNjUuODk5IDg3LjY0MDkgMTY2LjAyMSA4OC4zODY3IDE2Ni4wMjEgODkuMjc1OVY5Ni4wMDk3SDE2My4yNjhaTTE1Ni4zOTQgOTYuMDA5N1Y4NS40NTcySDE1OS4xNDhMMTU5LjA0IDg4LjAyODFMMTU5LjE0OCA4OC4yNTRWOTYuMDA5N0gxNTYuMzk0WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTE3My40NDkgOTYuMjM1NkMxNzIuNTUyIDk2LjIzNTYgMTcxLjgzNSA5Ni4xMDI5IDE3MS4yOTcgOTUuODM3NkMxNzAuNzY3IDk1LjU2NSAxNzAuMzgzIDk1LjE1NjMgMTcwLjE0NiA5NC42MTEzQzE2OS45MSA5NC4wNjYzIDE2OS43OTEgOTMuMzk1OCAxNjkuNzkxIDkyLjU5OThWODYuNDQ2OUgxNzIuNTI0VjkyLjE5MUMxNzIuNTI0IDkyLjc2NDcgMTcyLjY1MyA5My4xODc4IDE3Mi45MTEgOTMuNDYwM0MxNzMuMTc2IDkzLjcyNTYgMTczLjYzOSA5My44NTgzIDE3NC4yOTggOTMuODU4M0MxNzQuNjg2IDkzLjg1ODMgMTc1LjA1OSA5My44MTg5IDE3NS40MTcgOTMuNzRDMTc1Ljc3NiA5My42NTM5IDE3Ni4xMDYgOTMuNTQyOCAxNzYuNDA3IDkzLjQwNjVMMTc2LjE3IDk1LjcwODVDMTc1LjgxMiA5NS44NzM0IDE3NS4zOTkgOTYuMDAyNSAxNzQuOTMzIDk2LjA5NTdDMTc0LjQ3NCA5Ni4xODg5IDE3My45NzkgOTYuMjM1NiAxNzMuNDQ5IDk2LjIzNTZaTTE2OC4yNjQgODcuNzE2MlY4NS41NDMzSDE3Ni4zMUwxNzYuMDczIDg3LjcxNjJIMTY4LjI2NFpNMTY5LjgyNCA4NS43NDc3TDE2OS44MTMgODIuOTUwOUwxNzIuNTU2IDgyLjY3MTJMMTcyLjQ0OCA4NS43NDc3SDE2OS44MjRaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTgzLjM2NiA5Ni4yNzg2QzE4MS43MzggOTYuMjc4NiAxODAuNTMgOTUuODQ4MyAxNzkuNzQxIDk0Ljk4NzhDMTc4Ljk1OSA5NC4xMjcyIDE3OC41NjkgOTIuOTE4OSAxNzguNTY5IDkxLjM2MjdWOTAuMDcxOUMxNzguNTY5IDg4LjUyMjkgMTc4Ljk2MyA4Ny4zMjE4IDE3OS43NTIgODYuNDY4NEMxODAuNTQxIDg1LjYxNSAxODEuNzQ1IDg1LjE4ODMgMTgzLjM2NiA4NS4xODgzQzE4My43ODkgODUuMTg4MyAxODQuMTg0IDg1LjIyNDIgMTg0LjU0OSA4NS4yOTU5QzE4NC45MjIgODUuMzYwNCAxODUuMjU5IDg1LjQ1MDEgMTg1LjU2IDg1LjU2NDhDMTg1Ljg2OSA4NS42Nzk1IDE4Ni4xMzggODUuODAxNSAxODYuMzY3IDg1LjkzMDVMMTg2LjU5MyA4OC4yNDMzQzE4Ni4yNDIgODguMDIwOSAxODUuODQ3IDg3LjgzNDUgMTg1LjQxIDg3LjY4MzlDMTg0Ljk4IDg3LjUzMzMgMTg0LjQ4MSA4Ny40NTggMTgzLjkxNSA4Ny40NThDMTgzLjAyNSA4Ny40NTggMTgyLjM3MyA4Ny42ODc1IDE4MS45NTcgODguMTQ2NEMxODEuNTQ4IDg4LjU5ODIgMTgxLjM0NCA4OS4yNTggMTgxLjM0NCA5MC4xMjU3VjkxLjI2NTlDMTgxLjM0NCA5Mi4xMjY1IDE4MS41NTUgOTIuNzg5OCAxODEuOTc4IDkzLjI1NTlDMTgyLjQwOSA5My43MjIgMTgzLjA2OCA5My45NTUxIDE4My45NTggOTMuOTU1MUMxODQuNTI0IDkzLjk1NTEgMTg1LjAyNiA5My44ODM0IDE4NS40NjQgOTMuNzRDMTg1LjkwMSA5My41ODk0IDE4Ni4zMSA5My40MDY1IDE4Ni42OSA5My4xOTE0TDE4Ni40NjQgOTUuNTE0OEMxODYuMTEzIDk1LjcxNTYgMTg1LjY3MiA5NS44OTEzIDE4NS4xNDEgOTYuMDQxOUMxODQuNjEgOTYuMTk5NyAxODQuMDE5IDk2LjI3ODYgMTgzLjM2NiA5Ni4yNzg2WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTE4OS41ODEgOTYuMDA5N1Y4MS43ODkySDE5Mi4zNDVWOTYuMDA5N0gxODkuNTgxWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTE5NS44ODggOTYuMDA5N1Y4NS40NTcySDE5OC42NDJWOTYuMDA5N0gxOTUuODg4Wk0xOTcuMjY1IDg0LjIwOTVDMTk2Ljc0MSA4NC4yMDk1IDE5Ni4zNTQgODQuMDg3NSAxOTYuMTAzIDgzLjg0MzdDMTk1Ljg1OSA4My41OTI3IDE5NS43MzcgODMuMjQ4NSAxOTUuNzM3IDgyLjgxMTFWODIuNzU3M0MxOTUuNzM3IDgyLjMxOTggMTk1Ljg1OSA4MS45NzU2IDE5Ni4xMDMgODEuNzI0NkMxOTYuMzU0IDgxLjQ3MzYgMTk2Ljc0MSA4MS4zNDgxIDE5Ny4yNjUgODEuMzQ4MUMxOTcuNzgxIDgxLjM0ODEgMTk4LjE2NSA4MS40NzM2IDE5OC40MTYgODEuNzI0NkMxOTguNjY3IDgxLjk3NTYgMTk4Ljc5MiA4Mi4zMTk4IDE5OC43OTIgODIuNzU3M1Y4Mi44MTExQzE5OC43OTIgODMuMjU1NyAxOTguNjY3IDgzLjU5OTkgMTk4LjQxNiA4My44NDM3QzE5OC4xNjUgODQuMDg3NSAxOTcuNzgxIDg0LjIwOTUgMTk3LjI2NSA4NC4yMDk1WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTIwNS44NDMgODEuNDU1N0MyMDYuNDAyIDgxLjQ1NTcgMjA2LjkxOCA4MS41MDIzIDIwNy4zOTIgODEuNTk1NkMyMDcuODY1IDgxLjY4ODggMjA4LjI4NCA4MS44MDM1IDIwOC42NSA4MS45Mzk4TDIwOC44OTcgODQuMDA1MUMyMDguNTg5IDgzLjkxMTggMjA4LjI2NiA4My44MzY1IDIwNy45MjkgODMuNzc5MkMyMDcuNTk5IDgzLjcxNDYgMjA3LjIzNCA4My42ODI0IDIwNi44MzIgODMuNjgyNEMyMDYuMzUyIDgzLjY4MjQgMjA1Ljk3MiA4My43MzYyIDIwNS42OTIgODMuODQzN0MyMDUuNDE5IDgzLjk1MTMgMjA1LjIyNiA4NC4xMDU1IDIwNS4xMTEgODQuMzA2M0MyMDQuOTk2IDg0LjUwNzEgMjA0LjkzOSA4NC43NDM3IDIwNC45MzkgODUuMDE2MlY4NS4wNDg1QzIwNC45MzkgODUuMjQyMSAyMDQuOTY4IDg1LjQyNSAyMDUuMDI1IDg1LjU5NzFDMjA1LjA4MiA4NS43NjkyIDIwNS4xNTEgODUuOTIzNCAyMDUuMjI5IDg2LjA1OTZMMjAzLjQzMyA4Ni4xMjQyVjg1LjgzMzdDMjAzLjEwMyA4NS42NzYgMjAyLjgyMyA4NS40NDI5IDIwMi41OTQgODUuMTM0NUMyMDIuMzY1IDg0LjgyNjIgMjAyLjI1IDg0LjQ0MjUgMjAyLjI1IDgzLjk4MzZWODMuOTI5OEMyMDIuMjUgODMuMTY5NiAyMDIuNTQgODIuNTY3MiAyMDMuMTIxIDgyLjEyMjZDMjAzLjcwOSA4MS42NzggMjA0LjYxNiA4MS40NTU3IDIwNS44NDMgODEuNDU1N1pNMjAyLjYxNiA5Ni4wMDk3Vjg2LjY5NDNIMjA1LjM1OFY5Ni4wMDk3SDIwMi42MTZaTTIwMS4wODggODguMDkyN1Y4NS45MDlMMjAzLjc1NiA4NS45MzA1TDIwNC44MzEgODUuOTA5SDIwOC44NzZMMjA4LjYzOSA4OC4wOTI3SDIwMS4wODhaTTIxMi43NDggODEuNDU1N0MyMTMuMzA4IDgxLjQ1NTcgMjEzLjgyNCA4MS41MDIzIDIxNC4yOTcgODEuNTk1NkMyMTQuNzcxIDgxLjY4ODggMjE1LjE5IDgxLjgwMzUgMjE1LjU1NiA4MS45Mzk4TDIxNS44MDMgODQuMDA1MUMyMTUuNDk1IDgzLjkxMTggMjE1LjE3MiA4My44MzY1IDIxNC44MzUgODMuNzc5MkMyMTQuNTA1IDgzLjcxNDYgMjE0LjE0IDgzLjY4MjQgMjEzLjczOCA4My42ODI0QzIxMy4yNTggODMuNjgyNCAyMTIuODc3IDgzLjczNjIgMjEyLjU5OCA4My44NDM3QzIxMi4zMjUgODMuOTUxMyAyMTIuMTMyIDg0LjEwNTUgMjEyLjAxNyA4NC4zMDYzQzIxMS45MDIgODQuNTA3MSAyMTEuODQ1IDg0Ljc0MzcgMjExLjg0NSA4NS4wMTYyVjg1LjA0ODVDMjExLjg0NSA4NS4yNDIxIDIxMS44NzQgODUuNDI1IDIxMS45MzEgODUuNTk3MUMyMTEuOTg4IDg1Ljc2OTIgMjEyLjA1NiA4NS45MjM0IDIxMi4xMzUgODYuMDU5NkwyMTAuMzM5IDg2LjEyNDJWODUuODMzN0MyMTAuMDA5IDg1LjY3NiAyMDkuNzI5IDg1LjQ0MjkgMjA5LjUgODUuMTM0NUMyMDkuMjcgODQuODI2MiAyMDkuMTU2IDg0LjQ0MjUgMjA5LjE1NiA4My45ODM2VjgzLjkyOThDMjA5LjE1NiA4My4xNjk2IDIwOS40NDYgODIuNTY3MiAyMTAuMDI3IDgyLjEyMjZDMjEwLjYxNSA4MS42NzggMjExLjUyMiA4MS40NTU3IDIxMi43NDggODEuNDU1N1pNMjA5LjUyMSA5Ni4wMDk3Vjg2LjY5NDNIMjEyLjI2NFY5Ni4wMDk3SDIwOS41MjFaTTIwNy45OTQgODguMDkyN1Y4NS45MDlMMjEwLjY2MiA4NS45MzA1TDIxMS43MzcgODUuOTA5SDIxNS43ODJMMjE1LjU0NSA4OC4wOTI3SDIwNy45OTRaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTQ2LjY3NCAxMTkuNzQ0VjExNi4yNDZIMTUwLjM5OUMxNTEuNDY3IDExNi4yNDYgMTUyLjI2OCAxMTUuOTU3IDE1Mi44MDIgMTE1LjM3OEMxNTMuMzM2IDExNC44IDE1My42MDMgMTEzLjk3MiAxNTMuNjAzIDExMi44OTVWMTA5LjQ3N0MxNTMuNjAzIDEwOC40IDE1My4zMzYgMTA3LjU3NyAxNTIuODAyIDEwNy4wMDhDMTUyLjI2OCAxMDYuNDI5IDE1MS40NjcgMTA2LjE0IDE1MC4zOTkgMTA2LjE0SDE0Ni42NjFWMTAyLjY4MkgxNTAuNTQ2QzE1Mi45NzYgMTAyLjY4MiAxNTQuNzkxIDEwMy4yNyAxNTUuOTkzIDEwNC40NDRDMTU3LjIwMyAxMDUuNjEgMTU3LjgwOSAxMDcuMjg4IDE1Ny44MDkgMTA5LjQ3N1YxMTIuOTA4QzE1Ny44MDkgMTE1LjEwNyAxNTcuMjA4IDExNi43OTggMTU2LjAwNiAxMTcuOTgxQzE1NC44MDUgMTE5LjE1NiAxNTIuOTg1IDExOS43NDQgMTUwLjU0NiAxMTkuNzQ0SDE0Ni42NzRaTTE0My43NTEgMTE5Ljc0NFYxMDIuNjgySDE0Ny44NjNWMTE5Ljc0NEgxNDMuNzUxWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTE2MS40MyAxMTkuNzQ0TDE2MS45NjQgMTAyLjY4MkgxNjcuNzg1TDE3MC42MDIgMTEyLjg1NUgxNzAuNzc1TDE3My41OTIgMTAyLjY4MkgxNzkuNDEzTDE3OS45NDcgMTE5Ljc0NEgxNzUuOTAyTDE3NS43ODIgMTE0LjE1TDE3NS42NjIgMTA3LjM0MUgxNzUuNDYxTDE3Mi41MzggMTE3Ljc0MUgxNjguODI2TDE2NS45MDMgMTA3LjM0MUgxNjUuNjg5TDE2NS41ODIgMTE0LjE2M0wxNjUuNDc1IDExOS43NDRIMTYxLjQzWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTE4Ny4zMjEgMTE5Ljc0NEwxODIuNjQ5IDEwMi42ODJIMTg2Ljk3NEwxOTAuMzI1IDExNi42MzNIMTkwLjYzMkwxOTMuOTk2IDEwMi42ODJIMTk4LjMwOEwxOTMuNjQ5IDExOS43NDRIMTg3LjMyMVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0xOTguNzg0IC0xMS43MzYzTDE5NS42MTEgLTguNzA2MThMMTkxLjMxMSAtOS42MDcwM0wxOTMuMjE1IC01LjYzNTA4TDE5MS4wNDUgLTEuODI2OTJMMTk1LjM4NiAtMi40MjA2NkwxOTguMzU0IDAuODM0NzAxTDE5OS4xMzIgLTMuNDg1MzFMMjAzLjE0NSAtNS4yODcwMkwxOTkuMjc2IC03LjM3NTM3TDE5OC43ODQgLTExLjczNjNaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMjIzLjcwMSAyLjc2MTMzTDIxOS42NjggNC41NDI1NkwyMTUuOTQxIDIuMjI5TDIxNi4zNzEgNi41ODk5NkwyMTMuMDE0IDkuNDM1ODVMMjE3LjMxMyAxMC4zNzc3TDIxOC45NzIgMTQuNDMxNUwyMjEuMTgzIDEwLjY0MzhMMjI1LjU2NCAxMC4zMTYyTDIyMi42NTcgNy4wNDAzOUwyMjMuNzAxIDIuNzYxMzNaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMjQyLjEyNyAyNC45MTQyTDIzNy43NDYgMjUuMjAwOUwyMzUuMDIzIDIxLjc2MTJMMjMzLjkzOCAyNi4wMTk4TDIyOS44MjIgMjcuNTM0OUwyMzMuNTI4IDI5Ljg2ODlMMjMzLjcxMiAzNC4yNzA4TDIzNy4wOTEgMzEuNDY1OUwyNDEuMzA4IDMyLjY1MzRMMjM5LjY5MSAyOC41NzkxTDI0Mi4xMjcgMjQuOTE0MloiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0yNTEuODcyIDUyLjAzODlMMjQ3LjY3NSA1MC44MTA1TDI0Ni4yODMgNDYuNjMzOEwyNDMuODA2IDUwLjI1NzdMMjM5LjQyNCA1MC4yNzgyTDI0Mi4xMDYgNTMuNzU4N0wyNDAuNzc2IDU3LjkzNTRMMjQ0LjkxMSA1Ni40NjEzTDI0OC40NzQgNTkuMDIwNkwyNDguMzUxIDU0LjYzOTFMMjUxLjg3MiA1Mi4wMzg5WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTI1MS43NzEgODAuODQ1OUwyNDguMjI5IDc4LjI0NTdMMjQ4LjM3MyA3My44NjQzTDI0NC43OSA3Ni40MjM1TDI0MC42NTQgNzQuOTQ5NEwyNDIuMDA1IDc5LjEyNjFMMjM5LjMwMyA4Mi42MDY3TDI0My43MDUgODIuNjI3MUwyNDYuMTgyIDg2LjI1MUwyNDcuNTU0IDgyLjA3NDNMMjUxLjc3MSA4MC44NDU5WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTI0MS44MDEgMTA3Ljg5NUwyMzkuMzY1IDEwNC4yNTFMMjQxLjAwMiAxMDAuMTU2TDIzNi43NjQgMTAxLjM2NEwyMzMuMzg2IDk4LjUzODZMMjMzLjIyMiAxMDIuOTRMMjI5LjQ5NiAxMDUuMjc1TDIzMy42MzIgMTA2LjgxTDIzNC42OTYgMTExLjA2OUwyMzcuNDIgMTA3LjYwOUwyNDEuODAxIDEwNy44OTVaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMjIzLjIxMSAxMjkuOTA0TDIyMi4xNjYgMTI1LjY0NUwyMjUuMDc0IDEyMi4zNDlMMjIwLjY5MiAxMjIuMDIxTDIxOC40ODEgMTE4LjIzM0wyMTYuODIzIDEyMi4zMDhMMjEyLjUyMyAxMjMuMjI5TDIxNS44ODEgMTI2LjA3NUwyMTUuNDUxIDEzMC40NTZMMjE5LjE3NyAxMjguMTQzTDIyMy4yMTEgMTI5LjkwNFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0xOTguMTkxIDE0NC4yMTRMMTk4LjY2MSAxMzkuODUzTDIwMi41MzEgMTM3Ljc2NUwxOTguNTM5IDEzNS45NjNMMTk3Ljc0IDEzMS42NDNMMTk0Ljc5MiAxMzQuODk4TDE5MC40MzEgMTM0LjMwNUwxOTIuNjIyIDEzOC4xMTNMMTkwLjcxOCAxNDIuMDY0TDE5NS4wMTcgMTQxLjE4NEwxOTguMTkxIDE0NC4yMTRaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTY5Ljc5NCAxNDkuMTA2TDE3MS43MzkgMTQ1LjE3NUwxNzYuMDggMTQ0LjU0MUwxNzIuOTI3IDE0MS40NjlMMTczLjY4NCAxMzcuMTQ5TDE2OS43OTQgMTM5LjE5N0wxNjUuOTA0IDEzNy4xNDlMMTY2LjY0MSAxNDEuNDY5TDE2My40ODggMTQ0LjU0MUwxNjcuODQ5IDE0NS4xNzVMMTY5Ljc5NCAxNDkuMTA2WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTE0MS40MTggMTQzLjk4OUwxNDQuNTkxIDE0MC45NThMMTQ4Ljg5MSAxNDEuODU5TDE0Ni45ODcgMTM3Ljg4N0wxNDkuMTc3IDEzNC4wNzlMMTQ0LjgxNiAxMzQuNjczTDE0MS44NjggMTMxLjQxN0wxNDEuMDcgMTM1LjczN0wxMzcuMDc3IDEzNy41NkwxNDAuOTQ3IDEzOS42MjhMMTQxLjQxOCAxNDMuOTg5WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTExNi41MiAxMjkuNDk1TDEyMC41MzMgMTI3LjczNEwxMjQuMjc5IDEzMC4wMjdMMTIzLjgyOSAxMjUuNjY2TDEyNy4xODcgMTIyLjgyTDEyMi44ODcgMTIxLjg5OUwxMjEuMjI5IDExNy44MjVMMTE5LjAxOCAxMjEuNjEyTDExNC42MzYgMTIxLjk0TDExNy41NjQgMTI1LjIxNkwxMTYuNTIgMTI5LjQ5NVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik05OC4wNzQgMTA3LjM0TDEwMi40NTUgMTA3LjA1NEwxMDUuMTc4IDExMC41MTRMMTA2LjI2NCAxMDYuMjU1TDExMC4zNzkgMTA0LjcyTDEwNi42NzMgMTAyLjM4NkwxMDYuNTA5IDk3Ljk4MzlMMTAzLjEzMSAxMDAuNzg5TDk4Ljg5MjkgOTkuNjAxM0wxMDAuNTEgMTAzLjY5Nkw5OC4wNzQgMTA3LjM0WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTg4LjMyNzYgODAuMjEyMUw5Mi41NDUzIDgxLjQ2MUw5My45MTcgODUuNjE3Mkw5Ni4zOTQ0IDgxLjk5MzNMMTAwLjc5NiA4MS45NzI5TDk4LjA5MzcgNzguNDkyM0w5OS40NDUgNzQuMzE1Nkw5NS4zMDkzIDc1Ljc4OTdMOTEuNzI2MyA3My4yMzA1TDkxLjg2OTYgNzcuNjMyNEw4OC4zMjc2IDgwLjIxMjFaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNODguNDMwNyA1MS40MDU2TDkxLjk3MjcgNTQuMDA1OEw5MS44NDk4IDU4LjM4NzJMOTUuNDEyMyA1NS44MjhMOTkuNTQ4IDU3LjMwMjFMOTguMjE3MiA1My4xMjU0TDEwMC44OTkgNDkuNjQ0OUw5Ni40OTc0IDQ5LjYyNDRMOTQuMDQwNSA0Ni4wMDA1TDkyLjY0ODMgNTAuMTc3Mkw4OC40MzA3IDUxLjQwNTZaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNOTguMzk5OSAyNC4zNkwxMDAuODM2IDI4LjAwNDRMOTkuMjE4OSAzMi4wOTkyTDEwMy40MzcgMzAuOTExN0wxMDYuODE1IDMzLjcxNjZMMTA2Ljk5OSAyOS4zMTQ3TDExMC43MDUgMjYuOTgwN0wxMDYuNTg5IDI1LjQ0NTFMMTA1LjUwNCAyMS4xODY1TDEwMi43ODEgMjQuNjQ2Nkw5OC4zOTk5IDI0LjM2WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTExNy4wMTIgMi4zNTE2NkwxMTguMDU2IDYuNjMwNzJMMTE1LjEyOCA5LjkwNjU2TDExOS41MSAxMC4yMzQxTDEyMS43MjEgMTQuMDIxOEwxMjMuNCA5Ljk0NzUxTDEyNy42NzkgOS4wMjYxOEwxMjQuMzIxIDYuMTgwM0wxMjQuNzcyIDEuODE5MzRMMTIxLjAyNSA0LjExMjQyTDExNy4wMTIgMi4zNTE2NloiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0xNDIuMDMgLTExLjk2MTRMMTQxLjUzOSAtNy42MDA0N0wxMzcuNjY5IC01LjUxMjEyTDE0MS42ODIgLTMuNzEwNDFMMTQyLjQ2IDAuNjA5NjAzTDE0NS40MjkgLTIuNjQ1NzZMMTQ5Ljc3IC0yLjA1MjAxTDE0Ny41OTkgLTUuODYwMThMMTQ5LjUwMyAtOS44MTE2NkwxNDUuMjA0IC04LjkzMTI4TDE0Mi4wMyAtMTEuOTYxNFoiIGZpbGw9IndoaXRlIi8+CjwvZz4KPGRlZnM+CjxjbGlwUGF0aCBpZD0iY2xpcDBfMTIxM181NTIwIj4KPHJlY3Qgd2lkdGg9IjI0NSIgaGVpZ2h0PSIxNDguNzUiIGZpbGw9IndoaXRlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDAuMzU2NDQ1KSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgpqaXNzdWVySWNvbqJmZm9ybWF0Y3N2Z2RkYXRhWTGYPHN2ZyB3aWR0aD0iMzMiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMyAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE2LjY2NjUgMzJDMjUuNTAzMSAzMiAzMi42NjY1IDI0LjgzNjYgMzIuNjY2NSAxNkMzMi42NjY1IDcuMTYzNDQgMjUuNTAzMSAwIDE2LjY2NjUgMEM3LjgyOTk1IDAgMC42NjY1MDQgNy4xNjM0NCAwLjY2NjUwNCAxNkMwLjY2NjUwNCAyNC44MzY2IDcuODI5OTUgMzIgMTYuNjY2NSAzMloiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTE2LjYxNzMgMzAuMjY0NEMyNC40ODY4IDMwLjI2NDQgMzAuODY2MiAyMy44ODUgMzAuODY2MiAxNi4wMTU1QzMwLjg2NjIgOC4xNDYwNSAyNC40ODY4IDEuNzY2NiAxNi42MTczIDEuNzY2NkM4Ljc0Nzg2IDEuNzY2NiAyLjM2ODQxIDguMTQ2MDUgMi4zNjg0MSAxNi4wMTU1QzIuMzY4NDEgMjMuODg1IDguNzQ3ODYgMzAuMjY0NCAxNi42MTczIDMwLjI2NDRaIiBmaWxsPSIjMDAzNDU5Ii8+CjxwYXRoIGQ9Ik0xNi42MTczIDEwLjQwNzdMMjUuMzMyMyAxMC44OTQ1TDE2LjYxNzMgMTEuMzgxNEw3LjkwMjM0IDEwLjg5NDVMMTYuNjE3MyAxMC40MDc3WiIgZmlsbD0iIzBBNEI3NyIvPgo8cGF0aCBkPSJNMTYuNjE3MyAyMS40OTA3TDcuOTAyMjcgMjEuMDAzOUwxNi42MTczIDIwLjUxNzFMMjUuMzMyMyAyMS4wMDM5TDE2LjYxNzMgMjEuNDkwN1oiIGZpbGw9IiMwQTRCNzciLz4KPHBhdGggZD0iTTE2LjYxNzMgOC44MzkzNkwyMy42MDQ1IDkuMzI2MThMMTYuNjE3MyA5LjgxMzAxTDkuNjMwMTMgOS4zMjYxOEwxNi42MTczIDguODM5MzZaIiBmaWxsPSIjMEE0Qjc3Ii8+CjxwYXRoIGQ9Ik0xNi42MTczIDIzLjA1OTFMOS42MzAxNiAyMi41NzIzTDE2LjYxNzMgMjIuMDg1NEwyMy42MDQ1IDIyLjU3MjNMMTYuNjE3MyAyMy4wNTkxWiIgZmlsbD0iIzBBNEI3NyIvPgo8cGF0aCBkPSJNMTYuNjE3MyA3LjI3MUwyMS42MzcyIDcuNzU3ODJMMTYuNjE3MyA4LjI0NDY1TDExLjU5NzQgNy43NTc4MkwxNi42MTczIDcuMjcxWiIgZmlsbD0iIzBBNEI3NyIvPgo8cGF0aCBkPSJNMTYuNjE3MyAyNC42MjdMMTEuNTk3NCAyNC4xNDAxTDE2LjYxNzMgMjMuNjUzM0wyMS42MzcyIDI0LjE0MDFMMTYuNjE3MyAyNC42MjdaIiBmaWxsPSIjMEE0Qjc3Ii8+CjxwYXRoIGQ9Ik0zMC4yNDMyIDE1Ljc4MkMzMC4yNDMyIDIzLjMwNTMgMjQuMTQxMSAyOS40MDM3IDE2LjYxNzggMjkuNDAzN0M5LjA5NDU2IDI5LjQwMzcgMi45OTYwOSAyMy4zMDUzIDIuOTk2MDkgMTUuNzgyQzIuOTk2MDkgMTUuMjYxOSAzLjAyNTQgMTQuNzQ5MSAzLjA4NCAxNC4yNDM3QzMuODQ5NTEgMjEuMDQxNyA5LjYxNDY3IDI2LjMyMzQgMTYuNjE3OCAyNi4zMjM0QzIzLjYyMSAyNi4zMjM0IDI5LjM4OTggMjEuMDQxNyAzMC4xNTUzIDE0LjI0MzdDMzAuMjEzOSAxNC43NDkxIDMwLjI0MzIgMTUuMjYxOSAzMC4yNDMyIDE1Ljc4MloiIGZpbGw9IiMwMDJENEQiLz4KPHBhdGggZD0iTTE0LjE4NjkgMTYuMTQxMkwxMS44Mjg5IDE0LjIzODRDMTEuNzY0MSAxNC4xODYzIDExLjcyNjMgMTQuMTA3NiAxMS43MjYzIDE0LjAyNDNMMTEuNzI2MyAxMi41NDkyQzExLjcyNjMgMTIuNDQyMiAxMS43ODg3IDEyLjM0NDYgMTEuODg1OSAxMi4yOTk1TDEzLjg1OTYgMTEuMzg0MUMxNC4wMDI0IDExLjMxODEgMTQuMDYwNiAxMS4xNDU5IDEzLjk4NzYgMTEuMDA2NUwxMy4zMzIzIDkuNzU4NTRDMTMuMzExMyA5LjcxOTE3IDEzLjMwMDcgOS42NzUyOSAxMy4zMDA3IDkuNjMwNTlMMTMuMzAwNyA4Ljk3NjA5QzEzLjMwMDcgOC44Nzg4OSAxMy4zNTI0IDguNzg4NjcgMTMuNDM2IDguNzM5MDVMMTcuNDY5NyA2LjM1ODQ3QzE3LjU2ODUgNi4zMDAyMyAxNy42OTMyIDYuMzA5MjYgMTcuNzgyNiA2LjM4MTQzTDIxLjYwNjcgOS40NzAyNUwyMS42MDY3IDExLjU4ODhDMjEuNjA2NyAxMS43NDA5IDIxLjQ4MzcgMTEuODY0IDIxLjMzMTUgMTEuODY0TDE5LjE1ODEgMTEuOTk5MkMxOS4wMDU5IDExLjk5OTIgMTguODgyOSAxMi4xMjIyIDE4Ljg4MjkgMTIuMjc0M0wxOC43MDM2IDE0LjMxODhDMTguNzAzNiAxNC40NzA5IDE4LjgyNjcgMTQuNTkzOSAxOC45Nzg4IDE0LjU5MzlDMTkuMTMxIDE0LjU5MzkgMTkuMjU0IDE0LjcxNyAxOS4yNTQgMTQuODY5MUwxOS4yNTQgMTYuNzMyNkMxOS4yNTQgMTYuODg0NyAxOS4xMzEgMTcuMDA3NyAxOC45Nzg4IDE3LjAwNzdMMTcuMTE0MSAxNy4wMDc3QzE3LjA4NzUgMTcuMDA3NyAxNy4wNjA4IDE3LjAwMzYgMTcuMDM1IDE2Ljk5NjNMMTQuMTg2NSAxNi4xNDEyIiBmaWxsPSIjMDBBOEU4Ii8+CjxwYXRoIGQ9Ik0yMC4yOTk2IDE0Ljc5MjNMMjAuOTAwMyAxNC4xMTVDMjAuOTI3OSAxNC4wODM4IDIwLjk0MDMgMTQuMDQyIDIwLjkzNDEgMTQuMDAwOEwyMC43NDkyIDEyLjc3MUMyMC43NDA5IDEyLjcxNTcgMjAuNzAwNiAxMi42NzA2IDIwLjY0NjYgMTIuNjU2MkwyMC4wNDQgMTIuNDk0N0MxOS45NjkgMTIuNDc0NiAxOS44OTIgMTIuNTE5MSAxOS44NzE5IDEyLjU5NDFMMTkuNDc1OCAxNC4wNzI1QzE5LjQ1NTcgMTQuMTQ3NSAxOS41MDAyIDE0LjIyNDUgMTkuNTc1MSAxNC4yNDQ2TDE5Ljg5NjQgMTQuMzMwN0MxOS45NDc4IDE0LjM0NDUgMTkuOTg3IDE0LjM4NjEgMTkuOTk3NyAxNC40MzgyTDIwLjA1NjggMTQuNzI3MkMyMC4wOCAxNC44NDA3IDIwLjIyMjggMTQuODc4OSAyMC4yOTk2IDE0Ljc5MjNaIiBmaWxsPSIjMDBBOEU4Ii8+CjxwYXRoIGQ9Ik05LjQ5MDQxIDIxLjAwNzhMOS41NjM0NCAxOC43ODU1SDEwLjIyMDhMMTAuNjM4MSAyMC4xOTkySDEwLjY2MDdMMTEuMDc2MyAxOC43ODU1SDExLjczMTlMMTEuODA2NyAyMS4wMDc4SDExLjM2NUwxMS4zNDQxIDIwLjIwNzlMMTEuMzI1IDE5LjI4NDVIMTEuMjk3MkwxMC44NjA3IDIwLjc0ODdIMTAuNDM2NEw5Ljk5ODE4IDE5LjI4NDVIOS45NzAzNUw5Ljk1MTIzIDIwLjIwOTZMOS45MzIxIDIxLjAwNzhIOS40OTA0MVpNMTMuMTAwMSAyMS4wNTQ4QzEyLjgzNTggMjEuMDU0OCAxMi42MzUzIDIwLjk4NjkgMTIuNDk4NSAyMC44NTEzQzEyLjM2MjggMjAuNzE1NyAxMi4yOTUgMjAuNTIyNyAxMi4yOTUgMjAuMjcyMlYyMC4wNDFDMTIuMjk1IDE5Ljc4OTQgMTIuMzYyOCAxOS41OTU4IDEyLjQ5ODUgMTkuNDYwMkMxMi42MzUzIDE5LjMyMzQgMTIuODM1OCAxOS4yNTUgMTMuMTAwMSAxOS4yNTVDMTMuMzYzMyAxOS4yNTUgMTMuNTYyNyAxOS4zMjM0IDEzLjY5ODMgMTkuNDYwMkMxMy44MzQgMTkuNTk1OCAxMy45MDE4IDE5Ljc4OTQgMTMuOTAxOCAyMC4wNDFWMjAuMjcyMkMxMy45MDE4IDIwLjUyMjcgMTMuODM0IDIwLjcxNTcgMTMuNjk4MyAyMC44NTEzQzEzLjU2MzggMjAuOTg2OSAxMy4zNjQ0IDIxLjA1NDggMTMuMTAwMSAyMS4wNTQ4Wk0xMy4xMDAxIDIwLjY5ODNDMTMuMjE2MSAyMC42OTgzIDEzLjMwNDIgMjAuNjYzNSAxMy4zNjQ0IDIwLjU5MzlDMTMuNDI1OSAyMC41MjQ0IDEzLjQ1NjYgMjAuNDI0NyAxMy40NTY2IDIwLjI5NDlWMjAuMDE4NEMxMy40NTY2IDE5Ljg4NjIgMTMuNDI1OSAxOS43ODUzIDEzLjM2NDQgMTkuNzE1OEMxMy4zMDQyIDE5LjY0NTEgMTMuMjE2MSAxOS42MDk3IDEzLjEwMDEgMTkuNjA5N0MxMi45ODMgMTkuNjA5NyAxMi44OTM4IDE5LjY0NTEgMTIuODMyMyAxOS43MTU4QzEyLjc3MjEgMTkuNzg1MyAxMi43NDE5IDE5Ljg4NjIgMTIuNzQxOSAyMC4wMTg0VjIwLjI5NDlDMTIuNzQxOSAyMC40MjQ3IDEyLjc3MjEgMjAuNTI0NCAxMi44MzIzIDIwLjU5MzlDMTIuODkzOCAyMC42NjM1IDEyLjk4MyAyMC42OTgzIDEzLjEwMDEgMjAuNjk4M1pNMTUuNTEyNCAyMS4wMDc4VjE5Ljk4MzZDMTUuNTEyNCAxOS45MTE3IDE1LjUwMjYgMTkuODQ5NyAxNS40ODI4IDE5Ljc5NzVDMTUuNDY0MyAxOS43NDUzIDE1LjQzMyAxOS43MDQ4IDE1LjM4ODkgMTkuNjc1OEMxNS4zNDQ5IDE5LjY0NjggMTUuMjg0NiAxOS42MzIzIDE1LjIwODEgMTkuNjMyM0MxNS4xNDA5IDE5LjYzMjMgMTUuMDgxNyAxOS42NDQ1IDE1LjAzMDcgMTkuNjY4OEMxNC45ODA5IDE5LjY5MzIgMTQuOTM5NyAxOS43MjYyIDE0LjkwNzMgMTkuNzY4QzE0Ljg3NiAxOS44MDg1IDE0Ljg1MjIgMTkuODU0OSAxNC44MzYgMTkuOTA3MUwxNC43NjY0IDE5LjY2MzZIMTQuODQ5OUMxNC44Njg0IDE5LjU4ODMgMTQuODk5MSAxOS41MjA0IDE0Ljk0MiAxOS40NjAyQzE0Ljk4NjEgMTkuMzk5OSAxNS4wNDUyIDE5LjM1MjQgMTUuMTE5NCAxOS4zMTc2QzE1LjE5NDggMTkuMjgxNiAxNS4yODg3IDE5LjI2MzcgMTUuNDAxMSAxOS4yNjM3QzE1LjUzMjEgMTkuMjYzNyAxNS42MzgyIDE5LjI4ODYgMTUuNzE5MyAxOS4zMzg0QzE1LjgwMDUgMTkuMzg3MSAxNS44NjAyIDE5LjQ2MDIgMTUuODk4NSAxOS41NTc1QzE1LjkzNzkgMTkuNjU0OSAxNS45NTc2IDE5Ljc3NTUgMTUuOTU3NiAxOS45MTkyVjIxLjAwNzhIMTUuNTEyNFpNMTQuNDAxMiAyMS4wMDc4VjE5LjMwMTlIMTQuODQ2NEwxNC44MjkgMTkuNzE3NUwxNC44NDY0IDE5Ljc1NFYyMS4wMDc4SDE0LjQwMTJaTTE3LjE1ODMgMjEuMDQ0M0MxNy4wMTMzIDIxLjA0NDMgMTYuODk3NCAyMS4wMjI5IDE2LjgxMDUgMjAuOThDMTYuNzI0NyAyMC45MzU5IDE2LjY2MjcgMjAuODY5OSAxNi42MjQ0IDIwLjc4MThDMTYuNTg2MSAyMC42OTM2IDE2LjU2NyAyMC41ODUzIDE2LjU2NyAyMC40NTY2VjE5LjQ2MTlIMTcuMDA4N1YyMC4zOTA1QzE3LjAwODcgMjAuNDgzMiAxNy4wMjk2IDIwLjU1MTYgMTcuMDcxMyAyMC41OTU3QzE3LjExNDIgMjAuNjM4NiAxNy4xODkgMjAuNjYgMTcuMjk1NiAyMC42NkMxNy4zNTgyIDIwLjY2IDE3LjQxODUgMjAuNjUzNyAxNy40NzY1IDIwLjY0MDlDMTcuNTM0NCAyMC42MjcgMTcuNTg3OCAyMC42MDkgMTcuNjM2NSAyMC41ODdMMTcuNTk4MiAyMC45NTkxQzE3LjU0MDIgMjAuOTg1OCAxNy40NzM2IDIxLjAwNjcgMTcuMzk4MiAyMS4wMjE3QzE3LjMyNCAyMS4wMzY4IDE3LjI0NCAyMS4wNDQzIDE3LjE1ODMgMjEuMDQ0M1pNMTYuMzIwMSAxOS42NjcxVjE5LjMxNThIMTcuNjIwOEwxNy41ODI2IDE5LjY2NzFIMTYuMzIwMVpNMTYuNTcyMiAxOS4zNDg5TDE2LjU3MDUgMTguODk2OEwxNy4wMTM5IDE4Ljg1MTVMMTYuOTk2NSAxOS4zNDg5SDE2LjU3MjJaTTE4Ljc2MTUgMjEuMDUxM0MxOC40OTgzIDIxLjA1MTMgMTguMzAzIDIwLjk4MTcgMTguMTc1NSAyMC44NDI2QzE4LjA0OTEgMjAuNzAzNSAxNy45ODU5IDIwLjUwODIgMTcuOTg1OSAyMC4yNTY2VjIwLjA0NzlDMTcuOTg1OSAxOS43OTc1IDE4LjA0OTcgMTkuNjAzMyAxOC4xNzcyIDE5LjQ2NTRDMTguMzA0NyAxOS4zMjc0IDE4LjQ5OTUgMTkuMjU4NCAxOC43NjE1IDE5LjI1ODRDMTguODI5OSAxOS4yNTg0IDE4Ljg5MzcgMTkuMjY0MiAxOC45NTI4IDE5LjI3NThDMTkuMDEzMSAxOS4yODYzIDE5LjA2NzUgMTkuMzAwOCAxOS4xMTYyIDE5LjMxOTNDMTkuMTY2MSAxOS4zMzc5IDE5LjIwOTYgMTkuMzU3NiAxOS4yNDY3IDE5LjM3ODRMMTkuMjgzMiAxOS43NTIzQzE5LjIyNjQgMTkuNzE2NCAxOS4xNjI2IDE5LjY4NjIgMTkuMDkxOSAxOS42NjE5QzE5LjAyMjMgMTkuNjM3NSAxOC45NDE4IDE5LjYyNTQgMTguODUwMiAxOS42MjU0QzE4LjcwNjQgMTkuNjI1NCAxOC42MDA5IDE5LjY2MjUgMTguNTMzNyAxOS43MzY3QzE4LjQ2NzYgMTkuODA5NyAxOC40MzQ2IDE5LjkxNjMgMTguNDM0NiAyMC4wNTY2VjIwLjI0MDlDMTguNDM0NiAyMC4zODAxIDE4LjQ2ODggMjAuNDg3MyAxOC41MzcyIDIwLjU2MjZDMTguNjA2NyAyMC42MzggMTguNzEzNCAyMC42NzU3IDE4Ljg1NzEgMjAuNjc1N0MxOC45NDg3IDIwLjY3NTcgMTkuMDI5OSAyMC42NjQxIDE5LjEwMDYgMjAuNjQwOUMxOS4xNzEzIDIwLjYxNjYgMTkuMjM3NCAyMC41ODcgMTkuMjk4OCAyMC41NTIyTDE5LjI2MjMgMjAuOTI3OEMxOS4yMDU1IDIwLjk2MDMgMTkuMTM0MiAyMC45ODg3IDE5LjA0ODQgMjEuMDEzQzE4Ljk2MjYgMjEuMDM4NSAxOC44NjcgMjEuMDUxMyAxOC43NjE1IDIxLjA1MTNaTTE5Ljc2NjIgMjEuMDA3OFYxOC43MDg5SDIwLjIxMzFWMjEuMDA3OEgxOS43NjYyWk0yMC43ODU4IDIxLjAwNzhWMTkuMzAxOUgyMS4yMzA5VjIxLjAwNzhIMjAuNzg1OFpNMjEuMDA4NCAxOS4xMDAyQzIwLjkyMzcgMTkuMTAwMiAyMC44NjExIDE5LjA4MDUgMjAuODIwNSAxOS4wNDExQzIwLjc4MTEgMTkuMDAwNSAyMC43NjE0IDE4Ljk0NDkgMjAuNzYxNCAxOC44NzQxVjE4Ljg2NTRDMjAuNzYxNCAxOC43OTQ3IDIwLjc4MTEgMTguNzM5MSAyMC44MjA1IDE4LjY5ODVDMjAuODYxMSAxOC42NTc5IDIwLjkyMzcgMTguNjM3NiAyMS4wMDg0IDE4LjYzNzZDMjEuMDkxOCAxOC42Mzc2IDIxLjE1MzggMTguNjU3OSAyMS4xOTQ0IDE4LjY5ODVDMjEuMjM1IDE4LjczOTEgMjEuMjU1MyAxOC43OTQ3IDIxLjI1NTMgMTguODY1NFYxOC44NzQxQzIxLjI1NTMgMTguOTQ2IDIxLjIzNSAxOS4wMDE3IDIxLjE5NDQgMTkuMDQxMUMyMS4xNTM4IDE5LjA4MDUgMjEuMDkxOCAxOS4xMDAyIDIxLjAwODQgMTkuMTAwMlpNMjIuMzk1IDE4LjY1NUMyMi40ODU0IDE4LjY1NSAyMi41Njg5IDE4LjY2MjYgMjIuNjQ1NCAxOC42Nzc2QzIyLjcyMTkgMTguNjkyNyAyMi43ODk4IDE4LjcxMTMgMjIuODQ4OSAxOC43MzMzTDIyLjg4ODkgMTkuMDY3MkMyMi44MzkgMTkuMDUyMSAyMi43ODY5IDE5LjAzOTkgMjIuNzMyNCAxOS4wMzA2QzIyLjY3OSAxOS4wMjAyIDIyLjYxOTkgMTkuMDE1IDIyLjU1NSAxOS4wMTVDMjIuNDc3MyAxOS4wMTUgMjIuNDE1OSAxOS4wMjM3IDIyLjM3MDcgMTkuMDQxMUMyMi4zMjY2IDE5LjA1ODUgMjIuMjk1MyAxOS4wODM0IDIyLjI3NjggMTkuMTE1OUMyMi4yNTgyIDE5LjE0ODMgMjIuMjQ4OSAxOS4xODY2IDIyLjI0ODkgMTkuMjMwNlYxOS4yMzU4QzIyLjI0ODkgMTkuMjY3MSAyMi4yNTM2IDE5LjI5NjcgMjIuMjYyOSAxOS4zMjQ1QzIyLjI3MjEgMTkuMzUyNCAyMi4yODMxIDE5LjM3NzMgMjIuMjk1OSAxOS4zOTkzTDIyLjAwNTUgMTkuNDA5N1YxOS4zNjI4QzIxLjk1MjIgMTkuMzM3MyAyMS45MDcgMTkuMjk5NiAyMS44Njk5IDE5LjI0OThDMjEuODMyOCAxOS4xOTk5IDIxLjgxNDIgMTkuMTM3OSAyMS44MTQyIDE5LjA2MzdWMTkuMDU1QzIxLjgxNDIgMTguOTMyMSAyMS44NjEyIDE4LjgzNDcgMjEuOTU1MSAxOC43NjI5QzIyLjA1MDEgMTguNjkxIDIyLjE5NjggMTguNjU1IDIyLjM5NSAxOC42NTVaTTIxLjg3MzMgMjEuMDA3OFYxOS41MDE5SDIyLjMxNjhWMjEuMDA3OEgyMS44NzMzWk0yMS42MjY0IDE5LjcyOFYxOS4zNzVMMjIuMDU3NyAxOS4zNzg0TDIyLjIzMTYgMTkuMzc1SDIyLjg4NTRMMjIuODQ3MSAxOS43MjhIMjEuNjI2NFpNMjMuNTExNCAxOC42NTVDMjMuNjAxOCAxOC42NTUgMjMuNjg1MyAxOC42NjI2IDIzLjc2MTggMTguNjc3NkMyMy44MzgzIDE4LjY5MjcgMjMuOTA2MSAxOC43MTEzIDIzLjk2NTMgMTguNzMzM0wyNC4wMDUzIDE5LjA2NzJDMjMuOTU1NCAxOS4wNTIxIDIzLjkwMzIgMTkuMDM5OSAyMy44NDg4IDE5LjAzMDZDMjMuNzk1NCAxOS4wMjAyIDIzLjczNjMgMTkuMDE1IDIzLjY3MTQgMTkuMDE1QzIzLjU5MzcgMTkuMDE1IDIzLjUzMjMgMTkuMDIzNyAyMy40ODcxIDE5LjA0MTFDMjMuNDQzIDE5LjA1ODUgMjMuNDExNyAxOS4wODM0IDIzLjM5MzIgMTkuMTE1OUMyMy4zNzQ2IDE5LjE0ODMgMjMuMzY1MyAxOS4xODY2IDIzLjM2NTMgMTkuMjMwNlYxOS4yMzU4QzIzLjM2NTMgMTkuMjY3MSAyMy4zNyAxOS4yOTY3IDIzLjM3OTMgMTkuMzI0NUMyMy4zODg1IDE5LjM1MjQgMjMuMzk5NSAxOS4zNzczIDIzLjQxMjMgMTkuMzk5M0wyMy4xMjE5IDE5LjQwOTdWMTkuMzYyOEMyMy4wNjg2IDE5LjMzNzMgMjMuMDIzMyAxOS4yOTk2IDIyLjk4NjMgMTkuMjQ5OEMyMi45NDkyIDE5LjE5OTkgMjIuOTMwNiAxOS4xMzc5IDIyLjkzMDYgMTkuMDYzN1YxOS4wNTVDMjIuOTMwNiAxOC45MzIxIDIyLjk3NzYgMTguODM0NyAyMy4wNzE1IDE4Ljc2MjlDMjMuMTY2NSAxOC42OTEgMjMuMzEzMiAxOC42NTUgMjMuNTExNCAxOC42NTVaTTIyLjk4OTcgMjEuMDA3OFYxOS41MDE5SDIzLjQzMzJWMjEuMDA3OEgyMi45ODk3Wk0yMi43NDI4IDE5LjcyOFYxOS4zNzVMMjMuMTc0MSAxOS4zNzg0TDIzLjM0OCAxOS4zNzVIMjQuMDAxOEwyMy45NjM1IDE5LjcyOEgyMi43NDI4WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTEyLjgyOTkgMjQuODQ0MlYyNC4yNzg4SDEzLjQzMkMxMy42MDQ2IDI0LjI3ODggMTMuNzM0MSAyNC4yMzIgMTMuODIwNSAyNC4xMzg1QzEzLjkwNjggMjQuMDQ1IDEzLjk1IDIzLjkxMTIgMTMuOTUgMjMuNzM3MVYyMy4xODQ2QzEzLjk1IDIzLjAxMDUgMTMuOTA2OCAyMi44Nzc0IDEzLjgyMDUgMjIuNzg1M0MxMy43MzQxIDIyLjY5MTggMTMuNjA0NiAyMi42NDUxIDEzLjQzMiAyMi42NDUxSDEyLjgyNzdWMjIuMDg2MUgxMy40NTU3QzEzLjg0ODUgMjIuMDg2MSAxNC4xNDIgMjIuMTgxIDE0LjMzNjMgMjIuMzcxQzE0LjUzMTkgMjIuNTU5NCAxNC42Mjk4IDIyLjgzMDcgMTQuNjI5OCAyMy4xODQ2VjIzLjczOTJDMTQuNjI5OCAyNC4wOTQ2IDE0LjUzMjcgMjQuMzY4IDE0LjMzODQgMjQuNTU5NEMxNC4xNDQyIDI0Ljc0OTMgMTMuODUgMjQuODQ0MiAxMy40NTU3IDI0Ljg0NDJIMTIuODI5OVpNMTIuMzU3MiAyNC44NDQyVjIyLjA4NjFIMTMuMDIxOVYyNC44NDQySDEyLjM1NzJaTTE1LjIxNTMgMjQuODQ0MkwxNS4zMDE2IDIyLjA4NjFIMTYuMjQyNkwxNi42OTc5IDIzLjczMDZIMTYuNzI2TDE3LjE4MTQgMjIuMDg2MUgxOC4xMjIzTDE4LjIwODcgMjQuODQ0MkgxNy41NTQ3TDE3LjUzNTMgMjMuOTRMMTcuNTE1OSAyMi44MzkzSDE3LjQ4MzVMMTcuMDEwOSAyNC41MjA1SDE2LjQxMDlMMTUuOTM4MiAyMi44MzkzSDE1LjkwMzdMMTUuODg2NSAyMy45NDIxTDE1Ljg2OTIgMjQuODQ0MkgxNS4yMTUzWk0xOS40MDA3IDI0Ljg0NDJMMTguNjQ1NCAyMi4wODYxSDE5LjM0NDZMMTkuODg2MyAyNC4zNDE0SDE5LjkzNkwyMC40Nzk4IDIyLjA4NjFIMjEuMTc2OUwyMC40MjM3IDI0Ljg0NDJIMTkuNDAwN1oiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTE2LjY2OTcgMi43MjMxNEwxNi4zNTgxIDMuMzUyODNMMTUuNjYyOCAzLjQ1NDVMMTYuMTY0NiAzLjk0NjQ0TDE2LjA0NjYgNC42NDE3M0wxNi42Njk3IDQuMzEzNzZMMTcuMjkyOCA0LjY0MTczTDE3LjE3NDcgMy45NDY0NEwxNy42NzY1IDMuNDU0NUwxNi45ODEzIDMuMzUyODNMMTYuNjY5NyAyLjcyMzE0WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMjEuMjExOSAzLjU0MzQ2TDIwLjcwMzUgNC4wMjg4NEwyMC4wMTQ4IDMuODg0NTRMMjAuMzE5OCA0LjUyMDc5TDE5Ljk3MjIgNS4xMzA4TDIwLjY2NzQgNS4wMzU2OUwyMS4xNDMgNS41NTcxNUwyMS4yNjc2IDQuODY1MTVMMjEuOTEwNCA0LjU3NjU0TDIxLjI5MDYgNC4yNDIwMkwyMS4yMTE5IDMuNTQzNDZaIiBmaWxsPSIjREM4NTQ4Ii8+CjxwYXRoIGQ9Ik0yNS4yMDMyIDUuODY1NTRMMjQuNTU3MSA2LjE1MDg3TDIzLjk2MDIgNS43ODAyN0wyNC4wMjkxIDYuNDc4ODNMMjMuNDkxMiA2LjkzNDdMMjQuMTc5OSA3LjA4NTU2TDI0LjQ0NTYgNy43MzQ5M0wyNC43OTk4IDcuMTI4MkwyNS41MDE2IDcuMDc1NzNMMjUuMDM1OSA2LjU1MDk5TDI1LjIwMzIgNS44NjU1NFoiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTI4LjE1NDkgOS40MTM3NUwyNy40NTMxIDkuNDU5NjdMMjcuMDE2OSA4LjkwODY5TDI2Ljg0MyA5LjU5MDg1TDI2LjE4MzggOS44MzM1NUwyNi43Nzc0IDEwLjIwNzRMMjYuODA3IDEwLjkxMjVMMjcuMzQ4MSAxMC40NjMyTDI4LjAyMzcgMTAuNjUzNUwyNy43NjQ2IDEwLjAwMDhMMjguMTU0OSA5LjQxMzc1WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMjkuNzE1OSAxMy43NTk0TDI5LjA0MzYgMTMuNTYyNkwyOC44MjA2IDEyLjg5MzZMMjguNDIzOCAxMy40NzRMMjcuNzIxOSAxMy40NzczTDI4LjE1MTYgMTQuMDM0OUwyNy45Mzg0IDE0LjcwMzlMMjguNjAwOSAxNC40Njc4TDI5LjE3MTUgMTQuODc3N0wyOS4xNTE4IDE0LjE3NTlMMjkuNzE1OSAxMy43NTk0WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMjkuNjk5NyAxOC4zNzM3TDI5LjEzMjMgMTcuOTU3MkwyOS4xNTUzIDE3LjI1NTRMMjguNTgxMyAxNy42NjUzTDI3LjkxODggMTcuNDI5MkwyOC4xMzUzIDE4LjA5ODJMMjcuNzAyNCAxOC42NTU4TDI4LjQwNzUgMTguNjU5MUwyOC44MDQzIDE5LjIzOTVMMjkuMDI0MSAxOC41NzA1TDI5LjY5OTcgMTguMzczN1oiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTI4LjEwMjQgMjIuNzA2M0wyNy43MTIxIDIyLjEyMjVMMjcuOTc0NSAyMS40NjY2TDI3LjI5NTYgMjEuNjYwMUwyNi43NTQ1IDIxLjIwNzVMMjYuNzI4MiAyMS45MTI2TDI2LjEzMTMgMjIuMjg2NUwyNi43OTM4IDIyLjUzMjVMMjYuOTY0NCAyMy4yMTQ2TDI3LjQwMDYgMjIuNjYwNEwyOC4xMDI0IDIyLjcwNjNaIiBmaWxsPSIjREM4NTQ4Ii8+CjxwYXRoIGQ9Ik0yNS4xMjQ2IDI2LjIzMTdMMjQuOTU3MyAyNS41NDk1TDI1LjQyMyAyNS4wMjE1TDI0LjcyMTIgMjQuOTY5TDI0LjM2NyAyNC4zNjIzTDI0LjEwMTMgMjUuMDE0OUwyMy40MTI2IDI1LjE2MjVMMjMuOTUwNSAyNS42MTg0TDIzLjg4MTYgMjYuMzIwMkwyNC40Nzg1IDI1Ljk0OTZMMjUuMTI0NiAyNi4yMzE3WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMjEuMTE3IDI4LjUyNDRMMjEuMTkyNCAyNy44MjU5TDIxLjgxMjMgMjcuNDkxM0wyMS4xNzI4IDI3LjIwMjdMMjEuMDQ0OCAyNi41MTA3TDIwLjU3MjYgMjcuMDMyMkwxOS44NzQgMjYuOTM3MUwyMC4yMjQ5IDI3LjU0NzFMMTkuOTE5OSAyOC4xODAxTDIwLjYwODcgMjguMDM5TDIxLjExNyAyOC41MjQ0WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMTYuNTY4IDI5LjMwNzlMMTYuODc5NiAyOC42NzgyTDE3LjU3NDggMjguNTc2NUwxNy4wNjk4IDI4LjA4NDZMMTcuMTkxMSAyNy4zOTI2TDE2LjU2OCAyNy43MjA1TDE1Ljk0NDkgMjcuMzkyNkwxNi4wNjI5IDI4LjA4NDZMMTUuNTU3OSAyOC41NzY1TDE2LjI1NjQgMjguNjc4MkwxNi41NjggMjkuMzA3OVoiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTEyLjAyMjQgMjguNDg4M0wxMi41MzA4IDI4LjAwMjlMMTMuMjE5NSAyOC4xNDcyTDEyLjkxNDUgMjcuNTExTDEzLjI2NTQgMjYuOTAxTDEyLjU2NjggMjYuOTk2MUwxMi4wOTQ2IDI2LjQ3NDZMMTEuOTY2NyAyNy4xNjY2TDExLjMyNzEgMjcuNDU4NUwxMS45NDcgMjcuNzg5N0wxMi4wMjI0IDI4LjQ4ODNaIiBmaWxsPSIjREM4NTQ4Ii8+CjxwYXRoIGQ9Ik04LjAzNDM5IDI2LjE2NjNMOC42NzcyIDI1Ljg4NDJMOS4yNzczNyAyNi4yNTE1TDkuMjA1MjIgMjUuNTUzTDkuNzQzMDggMjUuMDk3MUw5LjA1NDM1IDI0Ljk0OTVMOC43ODg3IDI0LjI5NjlMOC40MzQ1MSAyNC45MDM2TDcuNzMyNjcgMjQuOTU2MUw4LjIwMTY1IDI1LjQ4MDhMOC4wMzQzOSAyNi4xNjYzWiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNNS4wNzk1OSAyMi42MTc0TDUuNzgxNDMgMjIuNTcxNUw2LjIxNzYyIDIzLjEyNThMNi4zOTE0NCAyMi40NDM2TDcuMDUwNjQgMjIuMTk3Nkw2LjQ1NzAzIDIxLjgyMzhMNi40MzA3OSAyMS4xMTg3TDUuODg5NjYgMjEuNTY4TDUuMjEwNzcgMjEuMzc3N0w1LjQ2OTg2IDIyLjAzMzdMNS4wNzk1OSAyMi42MTc0WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMy41MTgzMSAxOC4yNzIyTDQuMTkzOTEgMTguNDcyMkw0LjQxMzY1IDE5LjEzOEw0LjgxMDQ4IDE4LjU1NzVMNS41MTU2IDE4LjU1NDJMNS4wODI2OSAxNy45OTY3TDUuMjk5MTUgMTcuMzI3Nkw0LjYzNjY2IDE3LjU2MzhMNC4wNjI3MyAxNy4xNTM4TDQuMDg1NjkgMTcuODU4OUwzLjUxODMxIDE4LjI3MjJaIiBmaWxsPSIjREM4NTQ4Ii8+CjxwYXRoIGQ9Ik0zLjUzNDkxIDEzLjY1NzhMNC4xMDIyOSAxNC4wNzQzTDQuMDgyNjEgMTQuNzc2Mkw0LjY1MzI2IDE0LjM2NjJMNS4zMTU3NSAxNC42MDIzTDUuMTAyNTcgMTMuOTMzM0w1LjUzMjIgMTMuMzc1OEw0LjgyNzA4IDEzLjM3MjVMNC40MzM1MyAxMi43OTJMNC4yMTA1MSAxMy40NjFMMy41MzQ5MSAxMy42NTc4WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNNS4xMzIwOCA5LjMyNTI0TDUuNTIyMzYgOS45MDkwMUw1LjI2MzI3IDEwLjU2NDlMNS45Mzg4NyAxMC4zNzQ3TDYuNDgwMDEgMTAuODI0TDYuNTA5NTIgMTAuMTE4OUw3LjEwMzEzIDkuNzQ1MDNMNi40NDM5MyA5LjQ5OTA2TDYuMjcwMTEgOC44MTY4OUw1LjgzMzkyIDkuMzcxMTVMNS4xMzIwOCA5LjMyNTI0WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNOC4xMTMyNSA1Ljc5OTYzTDguMjgwNTEgNi40ODUwN0w3LjgxMTUyIDcuMDA5ODFMOC41MTMzNiA3LjA2MjI4TDguODY3NTYgNy42NjkwMUw5LjEzNjQ5IDcuMDE2MzdMOS44MjE5MyA2Ljg2ODc4TDkuMjg0MDcgNi40MTI5Mkw5LjM1NjIzIDUuNzE0MzZMOC43NTYwNiA2LjA4MTY3TDguMTEzMjUgNS43OTk2M1oiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTEyLjEyMDkgMy41MDczMkwxMi4wNDIyIDQuMjA1ODhMMTEuNDIyNCA0LjU0MDQxTDEyLjA2NTIgNC44MjkwMUwxMi4xODk4IDUuNTIxMDFMMTIuNjY1MyA0Ljk5OTU1TDEzLjM2MDYgNS4wOTQ2NkwxMy4wMTMgNC40ODQ2NUwxMy4zMTggMy44NTE2OEwxMi42MjkzIDMuOTkyNzFMMTIuMTIwOSAzLjUwNzMyWiIgZmlsbD0iI0RDODU0OCIvPgo8L3N2Zz4Kamlzc3VlckxvZ2+iZmZvcm1hdGNzdmdkZGF0YVmBojxzdmcgd2lkdGg9IjE0NSIgaGVpZ2h0PSI0MiIgdmlld0JveD0iMCAwIDE0NSA0MiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTIwLjk2OTkgNDEuOTdDMzIuNTUxMiA0MS45NyA0MS45Mzk3IDMyLjU4MTUgNDEuOTM5NyAyMS4wMDAxQzQxLjkzOTcgOS40MTg4IDMyLjU1MTIgMC4wMzAyNzM0IDIwLjk2OTkgMC4wMzAyNzM0QzkuMzg4NTMgMC4wMzAyNzM0IDAgOS40MTg4IDAgMjEuMDAwMUMwIDMyLjU4MTUgOS4zODg1MyA0MS45NyAyMC45Njk5IDQxLjk3WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMjAuOTA1MyAzOS42OTU0QzMxLjIxOTIgMzkuNjk1NCAzOS41ODAyIDMxLjMzNDQgMzkuNTgwMiAyMS4wMjA2QzM5LjU4MDIgMTAuNzA2NyAzMS4yMTkyIDIuMzQ1NyAyMC45MDUzIDIuMzQ1N0MxMC41OTE1IDIuMzQ1NyAyLjIzMDQ3IDEwLjcwNjcgMi4yMzA0NyAyMS4wMjA2QzIuMjMwNDcgMzEuMzM0NCAxMC41OTE1IDM5LjY5NTQgMjAuOTA1MyAzOS42OTU0WiIgZmlsbD0iIzAwMzQ1OSIvPgo8cGF0aCBkPSJNMjAuOTA1NCAxMy42NzA5TDMyLjMyNzUgMTQuMzA4OUwyMC45MDU0IDE0Ljk0N0w5LjQ4MzQgMTQuMzA4OUwyMC45MDU0IDEzLjY3MDlaIiBmaWxsPSIjMEE0Qjc3Ii8+CjxwYXRoIGQ9Ik0yMC45MDU0IDI4LjE5NjNMOS40ODMzNCAyNy41NTgyTDIwLjkwNTQgMjYuOTIwMkwzMi4zMjc0IDI3LjU1ODJMMjAuOTA1NCAyOC4xOTYzWiIgZmlsbD0iIzBBNEI3NyIvPgo8cGF0aCBkPSJNMjAuOTA1MyAxMS42MTUyTDMwLjA2MjggMTIuMjUzM0wyMC45MDUzIDEyLjg5MTNMMTEuNzQ3OCAxMi4yNTMzTDIwLjkwNTMgMTEuNjE1MloiIGZpbGw9IiMwQTRCNzciLz4KPHBhdGggZD0iTTIwLjkwNTIgMzAuMjUyTDExLjc0NzggMjkuNjEzOUwyMC45MDUyIDI4Ljk3NTlMMzAuMDYyNyAyOS42MTM5TDIwLjkwNTIgMzAuMjUyWiIgZmlsbD0iIzBBNEI3NyIvPgo8cGF0aCBkPSJNMjAuOTA1MyA5LjU2MDA2TDI3LjQ4NDUgMTAuMTk4MUwyMC45MDUzIDEwLjgzNjFMMTQuMzI2MiAxMC4xOTgxTDIwLjkwNTMgOS41NjAwNloiIGZpbGw9IiMwQTRCNzciLz4KPHBhdGggZD0iTTIwLjkwNTIgMzIuMzA3MUwxNC4zMjYgMzEuNjY5MUwyMC45MDUyIDMxLjAzMUwyNy40ODQ0IDMxLjY2OTFMMjAuOTA1MiAzMi4zMDcxWiIgZmlsbD0iIzBBNEI3NyIvPgo8cGF0aCBkPSJNMzguNzYzOCAyMC43MTQ5QzM4Ljc2MzggMzAuNTc1IDMwLjc2NjIgMzguNTY3OCAyMC45MDYxIDM4LjU2NzhDMTEuMDQ2IDM4LjU2NzggMy4wNTMyMiAzMC41NzUgMy4wNTMyMiAyMC43MTQ5QzMuMDUzMjIgMjAuMDMzMyAzLjA5MTYzIDE5LjM2MTIgMy4xNjg0MyAxOC42OTg3QzQuMTcxNzMgMjcuNjA4NCAxMS43Mjc2IDM0LjUzMDYgMjAuOTA2MSAzNC41MzA2QzMwLjA4NDYgMzQuNTMwNiAzNy42NDUzIDI3LjYwODQgMzguNjQ4NiAxOC42OTg3QzM4LjcyNTQgMTkuMzYxMiAzOC43NjM4IDIwLjAzMzMgMzguNzYzOCAyMC43MTQ5WiIgZmlsbD0iIzAwMkQ0RCIvPgo8cGF0aCBkPSJNMTcuNzQ5NyAyMS4xNzc0TDE0LjY4NzQgMTguNzA2M0MxNC42MDMyIDE4LjYzODYgMTQuNTU0MiAxOC41MzY0IDE0LjU1NDIgMTguNDI4M0wxNC41NTQyIDE2LjUxMjZDMTQuNTU0MiAxNi4zNzM2IDE0LjYzNTIgMTYuMjQ2OSAxNC43NjE0IDE2LjE4ODNMMTcuMzI0NyAxNC45OTk2QzE3LjUxIDE0LjkxMzggMTcuNTg1NiAxNC42OTAxIDE3LjQ5MDggMTQuNTA5MUwxNi42Mzk4IDEyLjg4ODVDMTYuNjEyNiAxMi44MzczIDE2LjU5ODggMTIuNzgwMyAxNi41OTg4IDEyLjcyMjNMMTYuNTk4OCAxMS44NzIzQzE2LjU5ODggMTEuNzQ2MSAxNi42NjU5IDExLjYyODkgMTYuNzc0NSAxMS41NjQ1TDIyLjAxMjkgOC40NzI4OUMyMi4xNDEzIDguMzk3MjcgMjIuMzAzMiA4LjQwODk4IDIyLjQxOTMgOC41MDI3MkwyNy4zODU1IDEyLjUxNDFMMjcuMzg1NSAxNS4yNjUzQzI3LjM4NTUgMTUuNDYyOSAyNy4yMjU3IDE1LjYyMjcgMjcuMDI4MSAxNS42MjI3TDI0LjIwNTYgMTUuNzk4M0MyNC4wMDggMTUuNzk4MyAyMy44NDgyIDE1Ljk1OCAyMy44NDgyIDE2LjE1NTZMMjMuNjE1NCAxOC44MTA3QzIzLjYxNTQgMTkuMDA4MyAyMy43NzUyIDE5LjE2OCAyMy45NzI4IDE5LjE2OEMyNC4xNzA0IDE5LjE2OCAyNC4zMzAxIDE5LjMyNzggMjQuMzMwMSAxOS41MjU0TDI0LjMzMDEgMjEuOTQ1NEMyNC4zMzAxIDIyLjE0MyAyNC4xNzA0IDIyLjMwMjggMjMuOTcyOCAyMi4zMDI4TDIxLjU1MTIgMjIuMzAyOEMyMS41MTY1IDIyLjMwMjggMjEuNDgxOSAyMi4yOTc0IDIxLjQ0ODQgMjIuMjg3OEwxNy43NDkxIDIxLjE3NzQiIGZpbGw9IiMwMEE4RTgiLz4KPHBhdGggZD0iTTI1LjY4NzkgMTkuNDI1NUwyNi40NjggMTguNTQ1OUMyNi41MDM4IDE4LjUwNTQgMjYuNTE5OSAxOC40NTExIDI2LjUxMTkgMTguMzk3NkwyNi4yNzE3IDE2LjgwMDVDMjYuMjYwOSAxNi43Mjg3IDI2LjIwODYgMTYuNjcwMiAyNi4xMzg1IDE2LjY1MTRMMjUuMzU1OSAxNi40NDE3QzI1LjI1ODYgMTYuNDE1NiAyNS4xNTg1IDE2LjQ3MzQgMjUuMTMyNCAxNi41NzA3TDI0LjYxOCAxOC40OTA3QzI0LjU5MTkgMTguNTg4MSAyNC42NDk3IDE4LjY4ODEgMjQuNzQ3IDE4LjcxNDJMMjUuMTY0MiAxOC44MjZDMjUuMjMxIDE4Ljg0MzkgMjUuMjgxOSAxOC44OTggMjUuMjk1OCAxOC45NjU3TDI1LjM3MjYgMTkuMzQxQzI1LjQwMjcgMTkuNDg4MyAyNS41ODgxIDE5LjUzOCAyNS42ODc5IDE5LjQyNTVaIiBmaWxsPSIjMDBBOEU4Ii8+CjxwYXRoIGQ9Ik0xMS42NTA2IDI3LjMwMjdMMTEuNzQ1NCAyNC40MTY2SDEyLjU5OUwxMy4xNDEgMjYuMjUyNkgxMy4xNzA0TDEzLjcxMDEgMjQuNDE2NkgxNC41NjE1TDE0LjY1ODYgMjcuMzAyN0gxNC4wODVMMTQuMDU3OSAyNi4yNjM5TDE0LjAzMzEgMjUuMDY0OEgxMy45OTY5TDEzLjQzMDEgMjYuOTY2MkgxMi44NzkxTDEyLjMxIDI1LjA2NDhIMTIuMjczOEwxMi4yNDkgMjYuMjY2MkwxMi4yMjQyIDI3LjMwMjdIMTEuNjUwNlpNMTYuMzM4NCAyNy4zNjM3QzE1Ljk5NTEgMjcuMzYzNyAxNS43MzQ3IDI3LjI3NTYgMTUuNTU3IDI3LjA5OTVDMTUuMzgwOSAyNi45MjMzIDE1LjI5MjggMjYuNjcyNyAxNS4yOTI4IDI2LjM0NzVWMjYuMDQ3MUMxNS4yOTI4IDI1LjcyMDQgMTUuMzgwOSAyNS40NjkgMTUuNTU3IDI1LjI5MjlDMTUuNzM0NyAyNS4xMTUyIDE1Ljk5NTEgMjUuMDI2NCAxNi4zMzg0IDI1LjAyNjRDMTYuNjgwMSAyNS4wMjY0IDE2LjkzOTEgMjUuMTE1MiAxNy4xMTUyIDI1LjI5MjlDMTcuMjkxNCAyNS40NjkgMTcuMzc5NSAyNS43MjA0IDE3LjM3OTUgMjYuMDQ3MVYyNi4zNDc1QzE3LjM3OTUgMjYuNjcyNyAxNy4yOTE0IDI2LjkyMzMgMTcuMTE1MiAyNy4wOTk1QzE2Ljk0MDYgMjcuMjc1NiAxNi42ODE2IDI3LjM2MzcgMTYuMzM4NCAyNy4zNjM3Wk0xNi4zMzg0IDI2LjkwMDhDMTYuNDg4OSAyNi45MDA4IDE2LjYwMzQgMjYuODU1NiAxNi42ODE2IDI2Ljc2NTNDMTYuNzYxNCAyNi42NzQ5IDE2LjgwMTMgMjYuNTQ1NSAxNi44MDEzIDI2LjM3NjhWMjYuMDE3OEMxNi44MDEzIDI1Ljg0NjEgMTYuNzYxNCAyNS43MTUyIDE2LjY4MTYgMjUuNjI0OEMxNi42MDM0IDI1LjUzMyAxNi40ODg5IDI1LjQ4NzEgMTYuMzM4NCAyNS40ODcxQzE2LjE4NjMgMjUuNDg3MSAxNi4wNzA0IDI1LjUzMyAxNS45OTA2IDI1LjYyNDhDMTUuOTEyMyAyNS43MTUyIDE1Ljg3MzIgMjUuODQ2MSAxNS44NzMyIDI2LjAxNzhWMjYuMzc2OEMxNS44NzMyIDI2LjU0NTUgMTUuOTEyMyAyNi42NzQ5IDE1Ljk5MDYgMjYuNzY1M0MxNi4wNzA0IDI2Ljg1NTYgMTYuMTg2MyAyNi45MDA4IDE2LjMzODQgMjYuOTAwOFpNMTkuNDcxMSAyNy4zMDI3VjI1Ljk3MjZDMTkuNDcxMSAyNS44NzkzIDE5LjQ1ODMgMjUuNzk4NyAxOS40MzI3IDI1LjczMUMxOS40MDg3IDI1LjY2MzIgMTkuMzY4IDI1LjYxMDUgMTkuMzEwOCAyNS41NzI5QzE5LjI1MzYgMjUuNTM1MiAxOS4xNzUzIDI1LjUxNjQgMTkuMDc1OSAyNS41MTY0QzE4Ljk4ODYgMjUuNTE2NCAxOC45MTE4IDI1LjUzMjIgMTguODQ1NiAyNS41NjM4QzE4Ljc4MDggMjUuNTk1NSAxOC43Mjc0IDI1LjYzODQgMTguNjg1MiAyNS42OTI2QzE4LjY0NDYgMjUuNzQ1MyAxOC42MTM3IDI1LjgwNTUgMTguNTkyNyAyNS44NzMyTDE4LjUwMjMgMjUuNTU3MUgxOC42MTA3QzE4LjYzNDggMjUuNDU5MiAxOC42NzQ3IDI1LjM3MTEgMTguNzMwNCAyNS4yOTI5QzE4Ljc4NzYgMjUuMjE0NiAxOC44NjQ0IDI1LjE1MjggMTguOTYwOCAyNS4xMDc3QzE5LjA1ODYgMjUuMDYxIDE5LjE4MDYgMjUuMDM3NyAxOS4zMjY2IDI1LjAzNzdDMTkuNDk2NyAyNS4wMzc3IDE5LjYzNDUgMjUuMDcgMTkuNzM5OSAyNS4xMzQ4QzE5Ljg0NTMgMjUuMTk4IDE5LjkyMjggMjUuMjkyOSAxOS45NzI1IDI1LjQxOTNDMjAuMDIzNyAyNS41NDU4IDIwLjA0OTMgMjUuNzAyNCAyMC4wNDkzIDI1Ljg4OVYyNy4zMDI3SDE5LjQ3MTFaTTE4LjAyODEgMjcuMzAyN1YyNS4wODczSDE4LjYwNjJMMTguNTgzNiAyNS42MjcxTDE4LjYwNjIgMjUuNjc0NVYyNy4zMDI3SDE4LjAyODFaTTIxLjYwODUgMjcuMzUwMkMyMS40MjAzIDI3LjM1MDIgMjEuMjY5OCAyNy4zMjIzIDIxLjE1NjkgMjcuMjY2NkMyMS4wNDU1IDI3LjIwOTQgMjAuOTY0OSAyNy4xMjM2IDIwLjkxNTIgMjcuMDA5MkMyMC44NjU2IDI2Ljg5NDcgMjAuODQwNyAyNi43NTQgMjAuODQwNyAyNi41ODY5VjI1LjI5NTFIMjEuNDE0M1YyNi41MDFDMjEuNDE0MyAyNi42MjE1IDIxLjQ0MTQgMjYuNzEwMyAyMS40OTU2IDI2Ljc2NzVDMjEuNTUxMyAyNi44MjMyIDIxLjY0ODQgMjYuODUxMSAyMS43ODY5IDI2Ljg1MTFDMjEuODY4MiAyNi44NTExIDIxLjk0NjUgMjYuODQyOCAyMi4wMjE4IDI2LjgyNjJDMjIuMDk3MSAyNi44MDgyIDIyLjE2NjMgMjYuNzg0OCAyMi4yMjk2IDI2Ljc1NjJMMjIuMTc5OSAyNy4yMzk1QzIyLjEwNDYgMjcuMjc0MSAyMi4wMTggMjcuMzAxMiAyMS45MjAyIDI3LjMyMDhDMjEuODIzOCAyNy4zNDA0IDIxLjcxOTkgMjcuMzUwMiAyMS42MDg1IDI3LjM1MDJaTTIwLjUyIDI1LjU2MTZWMjUuMTA1NEgyMi4yMDkyTDIyLjE1OTYgMjUuNTYxNkgyMC41MlpNMjAuODQ3NSAyNS4xNDgzTDIwLjg0NTIgMjQuNTYxMkwyMS40MjExIDI0LjUwMjVMMjEuMzk4NSAyNS4xNDgzSDIwLjg0NzVaTTIzLjY5MDYgMjcuMzU5MkMyMy4zNDg5IDI3LjM1OTIgMjMuMDk1MiAyNy4yNjg5IDIyLjkyOTYgMjcuMDg4MkMyMi43NjU1IDI2LjkwNzUgMjIuNjgzNCAyNi42NTM5IDIyLjY4MzQgMjYuMzI3MlYyNi4wNTYyQzIyLjY4MzQgMjUuNzMxIDIyLjc2NjIgMjUuNDc4OCAyMi45MzE4IDI1LjI5OTZDMjMuMDk3NCAyNS4xMjA1IDIzLjM1MDQgMjUuMDMwOSAyMy42OTA2IDI1LjAzMDlDMjMuNzc5NCAyNS4wMzA5IDIzLjg2MjIgMjUuMDM4NCAyMy45MzkgMjUuMDUzNUMyNC4wMTczIDI1LjA2NyAyNC4wODgxIDI1LjA4NTggMjQuMTUxMyAyNS4xMDk5QzI0LjIxNiAyNS4xMzQgMjQuMjcyNSAyNS4xNTk2IDI0LjMyMDcgMjUuMTg2N0wyNC4zNjgxIDI1LjY3MjJDMjQuMjk0MyAyNS42MjU2IDI0LjIxMTUgMjUuNTg2NCAyNC4xMTk3IDI1LjU1NDhDMjQuMDI5NCAyNS41MjMyIDIzLjkyNDcgMjUuNTA3NCAyMy44MDU4IDI1LjUwNzRDMjMuNjE5MSAyNS41MDc0IDIzLjQ4MjEgMjUuNTU1NiAyMy4zOTQ4IDI1LjY1MTlDMjMuMzA5IDI1Ljc0NjggMjMuMjY2MSAyNS44ODUzIDIzLjI2NjEgMjYuMDY3NFYyNi4zMDY4QzIzLjI2NjEgMjYuNDg3NSAyMy4zMTA1IDI2LjYyNjggMjMuMzk5MyAyNi43MjQ2QzIzLjQ4OTYgMjYuODIyNSAyMy42MjgxIDI2Ljg3MTQgMjMuODE0OCAyNi44NzE0QzIzLjkzMzggMjYuODcxNCAyNC4wMzkxIDI2Ljg1NjMgMjQuMTMxIDI2LjgyNjJDMjQuMjIyOCAyNi43OTQ2IDI0LjMwODYgMjYuNzU2MiAyNC4zODg0IDI2LjcxMTFMMjQuMzQxIDI3LjE5ODlDMjQuMjY3MiAyNy4yNDEgMjQuMTc0NiAyNy4yNzc5IDI0LjA2MzIgMjcuMzA5NUMyMy45NTE4IDI3LjM0MjYgMjMuODI3NiAyNy4zNTkyIDIzLjY5MDYgMjcuMzU5MlpNMjQuOTk1NCAyNy4zMDI3VjI0LjMxNzNIMjUuNTc1OFYyNy4zMDI3SDI0Ljk5NTRaTTI2LjMxOTUgMjcuMzAyN1YyNS4wODczSDI2Ljg5NzZWMjcuMzAyN0gyNi4zMTk1Wk0yNi42MDg1IDI0LjgyNTRDMjYuNDk4NiAyNC44MjU0IDI2LjQxNzMgMjQuNzk5OCAyNi4zNjQ2IDI0Ljc0ODZDMjYuMzEzNSAyNC42OTU5IDI2LjI4NzkgMjQuNjIzNiAyNi4yODc5IDI0LjUzMThWMjQuNTIwNUMyNi4yODc5IDI0LjQyODcgMjYuMzEzNSAyNC4zNTY0IDI2LjM2NDYgMjQuMzAzN0MyNi40MTczIDI0LjI1MSAyNi40OTg2IDI0LjIyNDcgMjYuNjA4NSAyNC4yMjQ3QzI2LjcxNjkgMjQuMjI0NyAyNi43OTc1IDI0LjI1MSAyNi44NTAyIDI0LjMwMzdDMjYuOTAyOSAyNC4zNTY0IDI2LjkyOTIgMjQuNDI4NyAyNi45MjkyIDI0LjUyMDVWMjQuNTMxOEMyNi45MjkyIDI0LjYyNTIgMjYuOTAyOSAyNC42OTc0IDI2Ljg1MDIgMjQuNzQ4NkMyNi43OTc1IDI0Ljc5OTggMjYuNzE2OSAyNC44MjU0IDI2LjYwODUgMjQuODI1NFpNMjguNDA5NCAyNC4yNDczQzI4LjUyNjggMjQuMjQ3MyAyOC42MzUyIDI0LjI1NyAyOC43MzQ1IDI0LjI3NjZDMjguODMzOSAyNC4yOTYyIDI4LjkyMiAyNC4zMjAzIDI4Ljk5ODggMjQuMzQ4OUwyOS4wNTA3IDI0Ljc4MjVDMjguOTg2IDI0Ljc2MjkgMjguOTE4MiAyNC43NDcxIDI4Ljg0NzUgMjQuNzM1MUMyOC43NzgyIDI0LjcyMTUgMjguNzAxNCAyNC43MTQ3IDI4LjYxNzEgMjQuNzE0N0MyOC41MTYyIDI0LjcxNDcgMjguNDM2NSAyNC43MjYgMjguMzc3NyAyNC43NDg2QzI4LjMyMDUgMjQuNzcxMiAyOC4yNzk5IDI0LjgwMzYgMjguMjU1OCAyNC44NDU3QzI4LjIzMTcgMjQuODg3OSAyOC4yMTk3IDI0LjkzNzUgMjguMjE5NyAyNC45OTQ4VjI1LjAwMTVDMjguMjE5NyAyNS4wNDIyIDI4LjIyNTcgMjUuMDgwNiAyOC4yMzc3IDI1LjExNjdDMjguMjQ5OCAyNS4xNTI4IDI4LjI2NDEgMjUuMTg1MiAyOC4yODA2IDI1LjIxMzhMMjcuOTAzNSAyNS4yMjc0VjI1LjE2NjRDMjcuODM0MiAyNS4xMzMzIDI3Ljc3NTUgMjUuMDg0MyAyNy43MjczIDI1LjAxOTZDMjcuNjc5MiAyNC45NTQ5IDI3LjY1NTEgMjQuODc0MyAyNy42NTUxIDI0Ljc3OFYyNC43NjY3QzI3LjY1NTEgMjQuNjA3MSAyNy43MTYxIDI0LjQ4MDYgMjcuODM4IDI0LjM4NzNDMjcuOTYxNSAyNC4yOTM5IDI4LjE1MTkgMjQuMjQ3MyAyOC40MDk0IDI0LjI0NzNaTTI3LjczMTkgMjcuMzAyN1YyNS4zNDcxSDI4LjMwNzdWMjcuMzAyN0gyNy43MzE5Wk0yNy40MTEyIDI1LjY0MDZWMjUuMTgyMkwyNy45NzEyIDI1LjE4NjdMMjguMTk3MSAyNS4xODIySDI5LjA0NjJMMjguOTk2NSAyNS42NDA2SDI3LjQxMTJaTTI5Ljg1OTIgMjQuMjQ3M0MyOS45NzY2IDI0LjI0NzMgMzAuMDg1IDI0LjI1NyAzMC4xODQ0IDI0LjI3NjZDMzAuMjgzNyAyNC4yOTYyIDMwLjM3MTggMjQuMzIwMyAzMC40NDg2IDI0LjM0ODlMMzAuNTAwNSAyNC43ODI1QzMwLjQzNTggMjQuNzYyOSAzMC4zNjggMjQuNzQ3MSAzMC4yOTczIDI0LjczNTFDMzAuMjI4IDI0LjcyMTUgMzAuMTUxMiAyNC43MTQ3IDMwLjA2NjkgMjQuNzE0N0MyOS45NjYxIDI0LjcxNDcgMjkuODg2MyAyNC43MjYgMjkuODI3NiAyNC43NDg2QzI5Ljc3MDMgMjQuNzcxMiAyOS43Mjk3IDI0LjgwMzYgMjkuNzA1NiAyNC44NDU3QzI5LjY4MTUgMjQuODg3OSAyOS42Njk1IDI0LjkzNzUgMjkuNjY5NSAyNC45OTQ4VjI1LjAwMTVDMjkuNjY5NSAyNS4wNDIyIDI5LjY3NTUgMjUuMDgwNiAyOS42ODc1IDI1LjExNjdDMjkuNjk5NiAyNS4xNTI4IDI5LjcxMzkgMjUuMTg1MiAyOS43MzA1IDI1LjIxMzhMMjkuMzUzMyAyNS4yMjc0VjI1LjE2NjRDMjkuMjg0MSAyNS4xMzMzIDI5LjIyNTMgMjUuMDg0MyAyOS4xNzcyIDI1LjAxOTZDMjkuMTI5IDI0Ljk1NDkgMjkuMTA0OSAyNC44NzQzIDI5LjEwNDkgMjQuNzc4VjI0Ljc2NjdDMjkuMTA0OSAyNC42MDcxIDI5LjE2NTkgMjQuNDgwNiAyOS4yODc4IDI0LjM4NzNDMjkuNDExMyAyNC4yOTM5IDI5LjYwMTcgMjQuMjQ3MyAyOS44NTkyIDI0LjI0NzNaTTI5LjE4MTcgMjcuMzAyN1YyNS4zNDcxSDI5Ljc1NzZWMjcuMzAyN0gyOS4xODE3Wk0yOC44NjEgMjUuNjQwNlYyNS4xODIyTDI5LjQyMTEgMjUuMTg2N0wyOS42NDY5IDI1LjE4MjJIMzAuNDk2TDMwLjQ0NjMgMjUuNjQwNkgyOC44NjFaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTUuOTg3NCAzMi4yODUyVjMxLjU1MDhIMTYuNzY5NEMxNi45OTM2IDMxLjU1MDggMTcuMTYxNyAzMS40OTAxIDE3LjI3MzkgMzEuMzY4N0MxNy4zODYgMzEuMjQ3MiAxNy40NDIgMzEuMDczNCAxNy40NDIgMzAuODQ3M1YzMC4xMjk4QzE3LjQ0MiAyOS45MDM3IDE3LjM4NiAyOS43MzA5IDE3LjI3MzkgMjkuNjExM0MxNy4xNjE3IDI5LjQ4OTkgMTYuOTkzNiAyOS40MjkxIDE2Ljc2OTQgMjkuNDI5MUgxNS45ODQ2VjI4LjcwMzJIMTYuODAwMkMxNy4zMTAzIDI4LjcwMzIgMTcuNjkxNSAyOC44MjY1IDE3Ljk0MzcgMjkuMDczMkMxOC4xOTc4IDI5LjMxOCAxOC4zMjQ5IDI5LjY3MDIgMTguMzI0OSAzMC4xMjk4VjMwLjg1MDFDMTguMzI0OSAzMS4zMTE3IDE4LjE5ODggMzEuNjY2NyAxNy45NDY1IDMxLjkxNTJDMTcuNjk0MyAzMi4xNjE4IDE3LjMxMjIgMzIuMjg1MiAxNi44MDAyIDMyLjI4NTJIMTUuOTg3NFpNMTUuMzczNiAzMi4yODUyVjI4LjcwMzJIMTYuMjM2OFYzMi4yODUySDE1LjM3MzZaTTE5LjA4NTIgMzIuMjg1MkwxOS4xOTczIDI4LjcwMzJIMjAuNDE5M0wyMS4wMTA3IDMwLjgzODlIMjEuMDQ3MkwyMS42Mzg1IDI4LjcwMzJIMjIuODYwNkwyMi45NzI3IDMyLjI4NTJIMjIuMTIzNEwyMi4wOTgyIDMxLjExMDhMMjIuMDczIDI5LjY4MTRIMjIuMDMwOUwyMS40MTcxIDMxLjg2NDdIMjAuNjM4TDIwLjAyNDIgMjkuNjgxNEgxOS45NzkzTDE5Ljk1NjkgMzEuMTEzNkwxOS45MzQ1IDMyLjI4NTJIMTkuMDg1MlpNMjQuNTIwOCAzMi4yODUyTDIzLjUzOTggMjguNzAzMkgyNC40NDc5TDI1LjE1MTQgMzEuNjMyMUgyNS4yMTU5TDI1LjkyMjIgMjguNzAzMkgyNi44Mjc1TDI1Ljg0OTMgMzIuMjg1MkgyNC41MjA4WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMjAuOTc0MSAzLjU5OTYxTDIwLjU2NTggNC40MjQ4OUwxOS42NTQ1IDQuNTU4MTRMMjAuMzEyMiA1LjIwMjg5TDIwLjE1NzQgNi4xMTQxM0wyMC45NzQxIDUuNjg0M0wyMS43OTA4IDYuMTE0MTNMMjEuNjM2MSA1LjIwMjg5TDIyLjI5MzcgNC41NTgxNEwyMS4zODI1IDQuNDI0ODlMMjAuOTc0MSAzLjU5OTYxWiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMjYuOTI3MyA0LjY3NDMyTDI2LjI2MSA1LjMxMDQ3TDI1LjM1ODQgNS4xMjEzNEwyNS43NTgxIDUuOTU1MjJMMjUuMzAyNSA2Ljc1NDcxTDI2LjIxMzcgNi42MzAwNkwyNi44MzcgNy4zMTM0OUwyNy4wMDAzIDYuNDA2NTRMMjcuODQyOCA2LjAyODI5TDI3LjAzMDQgNS41ODk4NkwyNi45MjczIDQuNjc0MzJaIiBmaWxsPSIjREM4NTQ4Ii8+CjxwYXRoIGQ9Ik0zMi4xNTgzIDcuNzE3MjNMMzEuMzExNSA4LjA5MTE4TDMwLjUyOTIgNy42MDU0N0wzMC42MTk1IDguNTIxMDFMMjkuOTE0NiA5LjExODQ4TDMwLjgxNzIgOS4zMTYyTDMxLjE2NTQgMTAuMTY3M0wzMS42Mjk2IDkuMzcyMDhMMzIuNTQ5NCA5LjMwMzMxTDMxLjkzOTEgOC42MTU1OEwzMi4xNTgzIDcuNzE3MjNaIiBmaWxsPSIjREM4NTQ4Ii8+CjxwYXRoIGQ9Ik0zNi4wMjY5IDEyLjM2ODVMMzUuMTA3MSAxMi40Mjg3TDM0LjUzNTQgMTEuNzA2NUwzNC4zMDc2IDEyLjYwMDZMMzMuNDQzNiAxMi45MTg3TDM0LjIyMTYgMTMuNDA4N0wzNC4yNjAzIDE0LjMzMjhMMzQuOTY5NSAxMy43NDRMMzUuODU1IDEzLjk5MzNMMzUuNTE1NCAxMy4xMzc5TDM2LjAyNjkgMTIuMzY4NVoiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTM4LjA3MjkgMTguMDYzNUwzNy4xOTE3IDE3LjgwNTZMMzYuODk5NCAxNi45Mjg3TDM2LjM3OTMgMTcuNjg5NUwzNS40NTk1IDE3LjY5MzhMMzYuMDIyNiAxOC40MjQ1TDM1Ljc0MzIgMTkuMzAxNEwzNi42MTE0IDE4Ljk5MTlMMzcuMzU5MyAxOS41MjkyTDM3LjMzMzUgMTguNjA5NEwzOC4wNzI5IDE4LjA2MzVaIiBmaWxsPSIjREM4NTQ4Ii8+CjxwYXRoIGQ9Ik0zOC4wNTEzIDI0LjExMDdMMzcuMzA3NyAyMy41NjQ5TDM3LjMzNzggMjIuNjQ1TDM2LjU4NTUgMjMuMTgyM0wzNS43MTczIDIyLjg3MjhMMzYuMDAxIDIzLjc0OTdMMzUuNDMzNiAyNC40ODA0TDM2LjM1NzcgMjQuNDg0N0wzNi44Nzc4IDI1LjI0NTVMMzcuMTY1OCAyNC4zNjg2TDM4LjA1MTMgMjQuMTEwN1oiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTM1Ljk1ODEgMjkuNzg5NUwzNS40NDY1IDI5LjAyNDRMMzUuNzkwNCAyOC4xNjQ4TDM0LjkwMDcgMjguNDE4NEwzNC4xOTE0IDI3LjgyNTJMMzQuMTU3MSAyOC43NDkzTDMzLjM3NDggMjkuMjM5M0wzNC4yNDMgMjkuNTYxN0wzNC40NjY1IDMwLjQ1NThMMzUuMDM4MiAyOS43Mjk0TDM1Ljk1ODEgMjkuNzg5NVoiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTMyLjA1NTIgMzQuNDFMMzEuODM2IDMzLjUxNkwzMi40NDY0IDMyLjgyMzlMMzEuNTI2NiAzMi43NTUyTDMxLjA2MjMgMzEuOTZMMzAuNzE0MiAzMi44MTUzTDI5LjgxMTUgMzMuMDA4OEwzMC41MTY0IDMzLjYwNjJMMzAuNDI2MiAzNC41MjYxTDMxLjIwODUgMzQuMDQwNEwzMi4wNTUyIDM0LjQxWiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMjYuODAyNyAzNy40MTQ2TDI2LjkwMTUgMzYuNDk5TDI3LjcxMzkgMzYuMDYwNkwyNi44NzU3IDM1LjY4MjNMMjYuNzA4MSAzNC43NzU0TDI2LjA4OTEgMzUuNDU4OEwyNS4xNzM2IDM1LjMzNDJMMjUuNjMzNSAzNi4xMzM3TDI1LjIzMzggMzYuOTYzMkwyNi4xMzY0IDM2Ljc3ODRMMjYuODAyNyAzNy40MTQ2WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMjAuODQwNyAzOC40NDE5TDIxLjI0OTEgMzcuNjE2NkwyMi4xNjAzIDM3LjQ4MzNMMjEuNDk4NCAzNi44Mzg2TDIxLjY1NzQgMzUuOTMxNkwyMC44NDA3IDM2LjM2MTVMMjAuMDI0IDM1LjkzMTZMMjAuMTc4OCAzNi44Mzg2TDE5LjUxNjggMzcuNDgzM0wyMC40MzI0IDM3LjYxNjZMMjAuODQwNyAzOC40NDE5WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMTQuODgzNCAzNy4zNjcyTDE1LjU0OTcgMzYuNzMxMUwxNi40NTIzIDM2LjkyMDJMMTYuMDUyNiAzNi4wODYzTDE2LjUxMjUgMzUuMjg2OEwxNS41OTY5IDM1LjQxMTVMMTQuOTc4IDM0LjcyOEwxNC44MTAzIDM1LjYzNUwxMy45NzIyIDM2LjAxNzVMMTQuNzg0NiAzNi40NTE3TDE0Ljg4MzQgMzcuMzY3MloiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTkuNjU2NjggMzQuMzI0MUwxMC40OTkxIDMzLjk1NDRMMTEuMjg1NyAzNC40MzU4TDExLjE5MTIgMzMuNTIwM0wxMS44OTYxIDMyLjkyMjhMMTAuOTkzNSAzMi43Mjk0TDEwLjY0NTMgMzEuODc0TDEwLjE4MTEgMzIuNjY5Mkw5LjI2MTIzIDMyLjczOEw5Ljg3NTg5IDMzLjQyNTdMOS42NTY2OCAzNC4zMjQxWiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNNS43ODM2OSAyOS42NzMzTDYuNzAzNTMgMjkuNjEzMUw3LjI3NTIxIDMwLjMzOTZMNy41MDMwMiAyOS40NDU1TDguMzY2OTkgMjkuMTIzMUw3LjU4ODk5IDI4LjYzMzFMNy41NTQ2IDI3LjcwOUw2Ljg0NTM4IDI4LjI5NzlMNS45NTU2MiAyOC4wNDg2TDYuMjk1MTkgMjguOTA4Mkw1Ljc4MzY5IDI5LjY3MzNaIiBmaWxsPSIjREM4NTQ4Ii8+CjxwYXRoIGQ9Ik0zLjczNzc5IDIzLjk3NzlMNC42MjMyNSAyNC4yNDAxTDQuOTExMjQgMjUuMTEyN0w1LjQzMTMzIDI0LjM1MTlMNi4zNTU0NyAyNC4zNDc2TDUuNzg4MSAyMy42MTY5TDYuMDcxNzkgMjIuNzRMNS4yMDM1MiAyMy4wNDk1TDQuNDUxMzIgMjIuNTEyMkw0LjQ4MTQgMjMuNDM2M0wzLjczNzc5IDIzLjk3NzlaIiBmaWxsPSIjREM4NTQ4Ii8+CjxwYXRoIGQ9Ik0zLjc1OTI4IDE3LjkzMDJMNC41MDI4OSAxOC40NzYxTDQuNDc3MSAxOS4zOTU5TDUuMjI1MDEgMTguODU4Nkw2LjA5MzI3IDE5LjE2ODFMNS44MTM4OCAxOC4yOTEyTDYuMzc2OTYgMTcuNTYwNUw1LjQ1MjgyIDE3LjU1NjJMNC45MzcwMiAxNi43OTU0TDQuNjQ0NzMgMTcuNjcyM0wzLjc1OTI4IDE3LjkzMDJaIiBmaWxsPSIjREM4NTQ4Ii8+CjxwYXRoIGQ9Ik01Ljg1MjU0IDEyLjI1MjJMNi4zNjQwNCAxMy4wMTczTDYuMDI0NDcgMTMuODc2OUw2LjkwOTkzIDEzLjYyNzZMNy42MTkxNSAxNC4yMTY1TDcuNjU3ODQgMTMuMjkyNEw4LjQzNTgzIDEyLjgwMjRMNy41NzE4NyAxMi40OEw3LjM0NDA2IDExLjU4NTlMNi43NzIzOCAxMi4zMTI0TDUuODUyNTQgMTIuMjUyMloiIGZpbGw9IiNEQzg1NDgiLz4KPHBhdGggZD0iTTkuNzU5NyA3LjYzMTc4TDkuOTc4OTIgOC41MzAxM0w5LjM2NDI2IDkuMjE3ODZMMTAuMjg0MSA5LjI4NjYzTDEwLjc0ODMgMTAuMDgxOEwxMS4xMDA4IDkuMjI2NDZMMTEuOTk5MSA5LjAzMzAzTDExLjI5NDIgOC40MzU1NkwxMS4zODg4IDcuNTIwMDJMMTAuNjAyMiA4LjAwMTQzTDkuNzU5NyA3LjYzMTc4WiIgZmlsbD0iI0RDODU0OCIvPgo8cGF0aCBkPSJNMTUuMDEyNSA0LjYyNjk1TDE0LjkwOTMgNS41NDI1TDE0LjA5NjkgNS45ODA5M0wxNC45Mzk0IDYuMzU5MThMMTUuMTAyNyA3LjI2NjEzTDE1LjcyNiA2LjU4MjY5TDE2LjYzNzIgNi43MDczNUwxNi4xODE2IDUuOTA3ODZMMTYuNTgxNCA1LjA3ODI4TDE1LjY3ODcgNS4yNjMxMUwxNS4wMTI1IDQuNjI2OTVaIiBmaWxsPSIjREM4NTQ4Ii8+CjxsaW5lIHgxPSI0OC45NzM4IiB5MT0iNC4zMjE3OCIgeDI9IjQ4Ljk3MzgiIHkyPSIzNy42Nzg0IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjAuNjExNjUzIi8+CjxwYXRoIGQ9Ik01Ny40MTEgMTguODkwNlYxNy40NjA2SDU5LjI5NkM1OS44ODk3IDE3LjQ2MDYgNjAuMzMzOCAxNy4zMDAzIDYwLjYyODUgMTYuOTc5NkM2MC45Mjc1IDE2LjY1NDYgNjEuMDc3IDE2LjE5MSA2MS4wNzcgMTUuNTg4NlYxMy44NjYxQzYxLjA3NyAxMy4yNjM4IDYwLjkyNzUgMTIuODAyMyA2MC42Mjg1IDEyLjQ4MTZDNjAuMzMzOCAxMi4xNjEgNTkuODg5NyAxMi4wMDA2IDU5LjI5NiAxMi4wMDA2SDU3LjQwNDVWMTAuNTgzNkg1OS4zNTQ1QzYwLjQ5ODUgMTAuNTgzNiA2MS4zNTg3IDEwLjg2NTMgNjEuOTM1IDExLjQyODZDNjIuNTExMyAxMS45OTIgNjIuNzk5NSAxMi44MDIzIDYyLjc5OTUgMTMuODU5NlYxNS41OTUxQzYyLjc5OTUgMTYuNjU2OCA2Mi41MTEzIDE3LjQ3MTUgNjEuOTM1IDE4LjAzOTFDNjEuMzYzIDE4LjYwNjggNjAuNTAyOCAxOC44OTA2IDU5LjM1NDUgMTguODkwNkg1Ny40MTFaTTU2LjIyMTUgMTguODkwNlYxMC41ODM2SDU3LjkwNVYxOC44OTA2SDU2LjIyMTVaTTY3LjA2NzggMTkuMDQ2NkM2NS45NzU4IDE5LjA0NjYgNjUuMTYzMyAxOC43OTUzIDY0LjYzMDMgMTguMjkyNkM2NC4wOTczIDE3Ljc5IDYzLjgzMDggMTcuMDYyIDYzLjgzMDggMTYuMTA4NlYxNS4yNzY2QzYzLjgzMDggMTQuMzMyIDY0LjA3OTkgMTMuNjA2MSA2NC41NzgzIDEzLjA5OTFDNjUuMDc2NiAxMi41OTIxIDY1LjgwMDMgMTIuMzM4NiA2Ni43NDkzIDEyLjMzODZDNjcuMzkwNiAxMi4zMzg2IDY3LjkyNTggMTIuNDUxMyA2OC4zNTQ4IDEyLjY3NjZDNjguNzgzOCAxMi45MDIgNjkuMTA0NCAxMy4yMjI2IDY5LjMxNjggMTMuNjM4NkM2OS41MzM0IDE0LjA1MDMgNjkuNjQxOCAxNC41NDQzIDY5LjY0MTggMTUuMTIwNlYxNS4zNDgxQzY5LjY0MTggMTUuNTA0MSA2OS42MzMxIDE1LjY2NDUgNjkuNjE1OCAxNS44MjkxQzY5LjYwMjggMTUuOTg5NSA2OS41ODMzIDE2LjE0MTEgNjkuNTU3MyAxNi4yODQxSDY4LjA0OTNDNjguMDYyMyAxNi4wNDU4IDY4LjA2ODggMTUuODIwNSA2OC4wNjg4IDE1LjYwODFDNjguMDczMSAxNS4zOTE1IDY4LjA3NTMgMTUuMTk2NSA2OC4wNzUzIDE1LjAyMzFDNjguMDc1MyAxNC43MjQxIDY4LjAyNzYgMTQuNDcwNiA2Ny45MzIzIDE0LjI2MjZDNjcuODM2OSAxNC4wNTAzIDY3LjY5MTggMTMuODkgNjcuNDk2OCAxMy43ODE2QzY3LjMwMTggMTMuNjczMyA2Ny4wNTI2IDEzLjYxOTEgNjYuNzQ5MyAxMy42MTkxQzY2LjMwMjkgMTMuNjE5MSA2NS45NzM2IDEzLjc0MjYgNjUuNzYxMyAxMy45ODk2QzY1LjU0ODkgMTQuMjM2NiA2NS40NDI4IDE0LjU4NzYgNjUuNDQyOCAxNS4wNDI2VjE1LjYzNDFMNjUuNDQ5MyAxNS44MjI2VjE2LjMyMzFDNjUuNDQ5MyAxNi41MjI1IDY1LjQ3OTYgMTYuNzA2NiA2NS41NDAzIDE2Ljg3NTZDNjUuNjA1MyAxNy4wNDQ2IDY1LjcxMTQgMTcuMTkyIDY1Ljg1ODggMTcuMzE3NkM2Ni4wMDYxIDE3LjQzOSA2Ni4yMDExIDE3LjUzNDMgNjYuNDQzOCAxNy42MDM2QzY2LjY5MDggMTcuNjczIDY2Ljk5ODQgMTcuNzA3NiA2Ny4zNjY4IDE3LjcwNzZDNjcuNzY1NCAxNy43MDc2IDY4LjE0NDYgMTcuNjY0MyA2OC41MDQzIDE3LjU3NzZDNjguODY4MyAxNy40ODY2IDY5LjIxMjggMTcuMzY1MyA2OS41Mzc4IDE3LjIxMzZMNjkuMzk0OCAxOC41MjY2QzY5LjEwNDQgMTguNjg3IDY4Ljc2MjEgMTguODEyNiA2OC4zNjc4IDE4LjkwMzZDNjcuOTc3OCAxOC45OTkgNjcuNTQ0NCAxOS4wNDY2IDY3LjA2NzggMTkuMDQ2NlpNNjQuNzE0OCAxNi4yODQxVjE1LjE3OTFINjkuMjE5M1YxNi4yODQxSDY0LjcxNDhaTTc0LjQ1MjUgMTkuMDQwMUM3NC4wNzExIDE5LjA0MDEgNzMuNzQ4MyAxOC45Nzk1IDczLjQ4NCAxOC44NTgxQzczLjIxOTYgMTguNzMyNSA3My4wMDUxIDE4LjU1NyA3Mi44NDA1IDE4LjMzMTZDNzIuNjgwMSAxOC4xMDYzIDcyLjU2NTMgMTcuODQyIDcyLjQ5NiAxNy41Mzg2SDcyLjAyMTVMNzIuNDQ0IDE2LjI2NDZDNzIuNDUyNiAxNi41NjggNzIuNTEzMyAxNi44MjM2IDcyLjYyNiAxNy4wMzE2QzcyLjc0MyAxNy4yMzk2IDcyLjkwMzMgMTcuMzk1NiA3My4xMDcgMTcuNDk5NkM3My4zMTUgMTcuNjAzNiA3My41NTc2IDE3LjY1NTYgNzMuODM1IDE3LjY1NTZDNzQuMjU1MyAxNy42NTU2IDc0LjU3NiAxNy41MzQzIDc0Ljc5NyAxNy4yOTE2Qzc1LjAxOCAxNy4wNDQ2IDc1LjEyODUgMTYuNjgwNiA3NS4xMjg1IDE2LjE5OTZWMTUuMTUzMUM3NS4xMjg1IDE0LjY3NjUgNzUuMDIwMSAxNC4zMTY4IDc0LjgwMzUgMTQuMDc0MUM3NC41ODY4IDEzLjgzMTUgNzQuMjY2MSAxMy43MTAxIDczLjg0MTUgMTMuNzEwMUM3My41OTg4IDEzLjcxMDEgNzMuMzggMTMuNzU3OCA3My4xODUgMTMuODUzMUM3Mi45OSAxMy45NDQxIDcyLjgyNzUgMTQuMDY3NiA3Mi42OTc1IDE0LjIyMzZDNzIuNTY3NSAxNC4zNzk2IDcyLjQ3NDMgMTQuNTU5NSA3Mi40MTggMTQuNzYzMUw3Mi4wMjggMTMuODY2MUg3Mi40ODk1QzcyLjU1ODggMTMuNTg4OCA3Mi42NjkzIDEzLjMzNzUgNzIuODIxIDEzLjExMjFDNzIuOTc3IDEyLjg4MjUgNzMuMTkxNSAxMi43MDI2IDczLjQ2NDUgMTIuNTcyNkM3My43NDE4IDEyLjQzODMgNzQuMDkwNiAxMi4zNzExIDc0LjUxMSAxMi4zNzExQzc1LjI2MDYgMTIuMzcxMSA3NS44MzA1IDEyLjYxMzggNzYuMjIwNSAxMy4wOTkxQzc2LjYxMDUgMTMuNTgwMSA3Ni44MDU1IDE0LjI5MyA3Ni44MDU1IDE1LjIzNzZWMTYuMTIxNkM3Ni44MDU1IDE3LjA3NSA3Ni42MDgzIDE3LjgwMDggNzYuMjE0IDE4LjI5OTFDNzUuODI0IDE4Ljc5MzEgNzUuMjM2OCAxOS4wNDAxIDc0LjQ1MjUgMTkuMDQwMVpNNzAuODEyNSAyMS4xMjY2VjEyLjUxNDFINzIuNDc2NUw3Mi40MTE1IDE0LjEzMjZMNzIuNDQ0IDE0LjQyNTFWMTYuOTc5Nkw3Mi40MjQ1IDE3LjI3ODZMNzIuNDcgMTkuMDI3MVYyMS4xMjY2SDcwLjgxMjVaTTgxLjcwMjcgMTguODkwNkw4MS43NjEyIDE3LjMzMDZMODEuNzE1NyAxNy4xODc2VjE1LjE5MjFMODEuNzA5MiAxNC45MDYxQzgxLjcwOTIgMTQuNDkwMSA4MS41OTQ0IDE0LjE4NDYgODEuMzY0NyAxMy45ODk2QzgxLjEzOTQgMTMuNzk0NiA4MC43Njg5IDEzLjY5NzEgODAuMjUzMiAxMy42OTcxQzc5LjgxNTUgMTMuNjk3MSA3OS40MDM5IDEzLjc1NTYgNzkuMDE4MiAxMy44NzI2Qzc4LjYzNjkgMTMuOTg1MyA3OC4yODM3IDE0LjExNzUgNzcuOTU4NyAxNC4yNjkxTDc4LjEwMTcgMTIuOTQzMUM3OC4yOTI0IDEyLjg0MzUgNzguNTA5IDEyLjc1MDMgNzguNzUxNyAxMi42NjM2Qzc4Ljk5ODcgMTIuNTcyNiA3OS4yNzM5IDEyLjQ5OSA3OS41NzcyIDEyLjQ0MjZDNzkuODgwNSAxMi4zODYzIDgwLjIwNzcgMTIuMzU4MSA4MC41NTg3IDEyLjM1ODFDODEuMDc4NyAxMi4zNTgxIDgxLjUxODUgMTIuNDIxIDgxLjg3ODIgMTIuNTQ2NkM4Mi4yMzc5IDEyLjY2OCA4Mi41MjM5IDEyLjg0MzUgODIuNzM2MiAxMy4wNzMxQzgyLjk1MjkgMTMuMzAyOCA4My4xMDg5IDEzLjU3OCA4My4yMDQyIDEzLjg5ODZDODMuMjk5NSAxNC4yMTUgODMuMzQ3MiAxNC41NjYgODMuMzQ3MiAxNC45NTE2VjE4Ljg5MDZIODEuNzAyN1pNNzkuNjE2MiAxOS4wNDAxQzc4Ljk4MzUgMTkuMDQwMSA3OC41MDA0IDE4Ljg4MiA3OC4xNjY3IDE4LjU2NTZDNzcuODM3NCAxOC4yNDkzIDc3LjY3MjcgMTcuNzk4NiA3Ny42NzI3IDE3LjIxMzZWMTcuMDMxNkM3Ny42NzI3IDE2LjQxMiA3Ny44NjM0IDE1Ljk1NDggNzguMjQ0NyAxNS42NjAxQzc4LjYyNiAxNS4zNjExIDc5LjIzMDUgMTUuMTU1MyA4MC4wNTgyIDE1LjA0MjZMODEuODY1MiAxNC43OTU2TDgxLjk2MjcgMTUuODY4MUw4MC4yOTg3IDE2LjEwODZDNzkuOTM0NyAxNi4xNTYzIDc5LjY3NDcgMTYuMjQzIDc5LjUxODcgMTYuMzY4NkM3OS4zNjcgMTYuNDk0MyA3OS4yOTEyIDE2LjY3ODUgNzkuMjkxMiAxNi45MjExVjE2Ljk4NjFDNzkuMjkxMiAxNy4yMjQ1IDc5LjM2NDkgMTcuNDEwOCA3OS41MTIyIDE3LjU0NTFDNzkuNjYzOSAxNy42NzUxIDc5LjkgMTcuNzQwMSA4MC4yMjA3IDE3Ljc0MDFDODAuNTA2NyAxNy43NDAxIDgwLjc1MTUgMTcuNjk0NiA4MC45NTUyIDE3LjYwMzZDODEuMTU4OSAxNy41MTI2IDgxLjMyNTcgMTcuMzkzNSA4MS40NTU3IDE3LjI0NjFDODEuNTkgMTcuMDk0NSA4MS42ODU0IDE2LjkyNTUgODEuNzQxNyAxNi43MzkxTDgxLjk3NTcgMTcuNTY0Nkg4MS42ODk3QzgxLjYyMDQgMTcuODM3NiA4MS41MDc3IDE4LjA4NjggODEuMzUxNyAxOC4zMTIxQzgxLjIgMTguNTMzMSA4MC45ODU1IDE4LjcxMDggODAuNzA4MiAxOC44NDUxQzgwLjQzMDkgMTguOTc1MSA4MC4wNjY5IDE5LjA0MDEgNzkuNjE2MiAxOS4wNDAxWk04Ni4zMzIzIDE1LjE4NTZMODUuOTA5OCAxNC4wNzQxSDg2LjMxMjhDODYuNDI5OCAxMy41NTg1IDg2LjY0MjIgMTMuMTUzMyA4Ni45NDk4IDEyLjg1ODZDODcuMjU3NSAxMi41NjQgODcuNjg0MyAxMi40MTY2IDg4LjIzMDMgMTIuNDE2NkM4OC4zNDMgMTIuNDE2NiA4OC40NDQ4IDEyLjQyNTMgODguNTM1OCAxMi40NDI2Qzg4LjYyNjggMTIuNDU1NiA4OC43MDkyIDEyLjQ3MyA4OC43ODI4IDEyLjQ5NDZMODguODczOCAxNC4xNTg2Qzg4Ljc3ODUgMTQuMTI4MyA4OC42NjggMTQuMTA2NiA4OC41NDIzIDE0LjA5MzZDODguNDE2NyAxNC4wNzYzIDg4LjI4NDUgMTQuMDY3NiA4OC4xNDU4IDE0LjA2NzZDODcuNzAzOCAxNC4wNjc2IDg3LjMyNjggMTQuMTY1MSA4Ny4wMTQ4IDE0LjM2MDFDODYuNzA3MiAxNC41NTUxIDg2LjQ3OTcgMTQuODMwMyA4Ni4zMzIzIDE1LjE4NTZaTTg0LjcxMzggMTguODkwNlYxMi41MTQxSDg2LjI5OThMODYuMjI4MyAxNC40NzA2TDg2LjM3NzggMTQuNTI5MVYxOC44OTA2SDg0LjcxMzhaTTkyLjQ3NzggMTkuMDI3MUM5MS45MzYxIDE5LjAyNzEgOTEuNTAyOCAxOC45NDcgOTEuMTc3OCAxOC43ODY2QzkwLjg1NzEgMTguNjIyIDkwLjYyNTMgMTguMzc1IDkwLjQ4MjMgMTguMDQ1NkM5MC4zMzkzIDE3LjcxNjMgOTAuMjY3OCAxNy4zMTExIDkwLjI2NzggMTYuODMwMVYxMy4xMTIxSDkxLjkxODhWMTYuNTgzMUM5MS45MTg4IDE2LjkyOTggOTEuOTk2OCAxNy4xODU1IDkyLjE1MjggMTcuMzUwMUM5Mi4zMTMxIDE3LjUxMDUgOTIuNTkyNiAxNy41OTA2IDkyLjk5MTMgMTcuNTkwNkM5My4yMjUzIDE3LjU5MDYgOTMuNDUwNiAxNy41NjY4IDkzLjY2NzMgMTcuNTE5MUM5My44ODQgMTcuNDY3MSA5NC4wODMzIDE3LjQgOTQuMjY1MyAxNy4zMTc2TDk0LjEyMjMgMTguNzA4NkM5My45MDU2IDE4LjgwODMgOTMuNjU2NSAxOC44ODYzIDkzLjM3NDggMTguOTQyNkM5My4wOTc1IDE4Ljk5OSA5Mi43OTg1IDE5LjAyNzEgOTIuNDc3OCAxOS4wMjcxWk04OS4zNDQ4IDEzLjg3OTFWMTIuNTY2MUg5NC4yMDY4TDk0LjA2MzggMTMuODc5MUg4OS4zNDQ4Wk05MC4yODczIDEyLjY4OTZMOTAuMjgwOCAxMC45OTk2TDkxLjkzODMgMTAuODMwNkw5MS44NzMzIDEyLjY4OTZIOTAuMjg3M1pNMTAzLjA4NCAxOC44OTA2VjE1LjAxNjZDMTAzLjA4NCAxNC43NTY2IDEwMy4wNTIgMTQuNTMzNSAxMDIuOTg3IDE0LjM0NzFDMTAyLjkyMiAxNC4xNTY1IDEwMi44MTMgMTQuMDA5MSAxMDIuNjYyIDEzLjkwNTFDMTAyLjUxNCAxMy44MDExIDEwMi4zMTMgMTMuNzQ5MSAxMDIuMDU3IDEzLjc0OTFDMTAxLjgyMyAxMy43NDkxIDEwMS42MTkgMTMuNzk0NiAxMDEuNDQ2IDEzLjg4NTZDMTAxLjI3MyAxMy45NzY2IDEwMS4xMzIgMTQuMTAwMSAxMDEuMDI0IDE0LjI1NjFDMTAwLjkxNSAxNC40MDc4IDEwMC44MzUgMTQuNTgxMSAxMDAuNzgzIDE0Ljc3NjFMMTAwLjYwMSAxMy44NjYxSDEwMC43N0MxMDAuODM5IDEzLjU5MzEgMTAwLjk1MiAxMy4zNDQgMTAxLjEwOCAxMy4xMTg2QzEwMS4yNjQgMTIuODg5IDEwMS40NzYgMTIuNzA3IDEwMS43NDUgMTIuNTcyNkMxMDIuMDE4IDEyLjQzODMgMTAyLjM2MyAxMi4zNzExIDEwMi43NzkgMTIuMzcxMUMxMDMuMjQyIDEyLjM3MTEgMTAzLjYxNyAxMi40NjIxIDEwMy45MDMgMTIuNjQ0MUMxMDQuMTkzIDEyLjgyMTggMTA0LjQwOCAxMy4wOTA1IDEwNC41NDcgMTMuNDUwMUMxMDQuNjg1IDEzLjgwNTUgMTA0Ljc1NSAxNC4yNDc1IDEwNC43NTUgMTQuNzc2MVYxOC44OTA2SDEwMy4wODRaTTk1LjIyNTYgMTguODkwNlYxMi41MTQxSDk2Ljg4OTZMOTYuODI0NiAxNC4xMzI2TDk2Ljg4OTYgMTQuMjA0MVYxOC44OTA2SDk1LjIyNTZaTTk5LjE1ODEgMTguODkwNlYxNS4wMTY2Qzk5LjE1ODEgMTQuNzU2NiA5OS4xMjU2IDE0LjUzMzUgOTkuMDYwNiAxNC4zNDcxQzk4Ljk5NTYgMTQuMTU2NSA5OC44ODcyIDE0LjAwOTEgOTguNzM1NiAxMy45MDUxQzk4LjU4ODIgMTMuODAxMSA5OC4zODY3IDEzLjc0OTEgOTguMTMxMSAxMy43NDkxQzk3Ljg5MjcgMTMuNzQ5MSA5Ny42ODY5IDEzLjc5NDYgOTcuNTEzNiAxMy44ODU2Qzk3LjM0NDYgMTMuOTc2NiA5Ny4yMDM3IDE0LjEwMDEgOTcuMDkxMSAxNC4yNTYxQzk2Ljk4MjcgMTQuNDA3OCA5Ni45MDI2IDE0LjU4MTEgOTYuODUwNiAxNC43NzYxTDk2LjU5MDYgMTMuODY2MUg5Ni45MDI2Qzk2Ljk2NzYgMTMuNTg0NSA5Ny4wNzU5IDEzLjMzMSA5Ny4yMjc2IDEzLjEwNTZDOTcuMzgzNiAxMi44ODAzIDk3LjU5MzcgMTIuNzAyNiA5Ny44NTgxIDEyLjU3MjZDOTguMTIyNCAxMi40MzgzIDk4LjQ1MTcgMTIuMzcxMSA5OC44NDYxIDEyLjM3MTFDOTkuNDM5NyAxMi4zNzExIDk5Ljg4NjEgMTIuNTIyOCAxMDAuMTg1IDEyLjgyNjFDMTAwLjQ4OCAxMy4xMjk1IDEwMC42NzkgMTMuNTcxNSAxMDAuNzU3IDE0LjE1MjFDMTAwLjc3NCAxNC4yMzg4IDEwMC43OSAxNC4zNDA2IDEwMC44MDMgMTQuNDU3NkMxMDAuODE2IDE0LjU3MDMgMTAwLjgyMiAxNC42NzY1IDEwMC44MjIgMTQuNzc2MVYxOC44OTA2SDk5LjE1ODFaTTEwOS4xNCAxOS4wNDY2QzEwOC4wNDggMTkuMDQ2NiAxMDcuMjM2IDE4Ljc5NTMgMTA2LjcwMyAxOC4yOTI2QzEwNi4xNyAxNy43OSAxMDUuOTAzIDE3LjA2MiAxMDUuOTAzIDE2LjEwODZWMTUuMjc2NkMxMDUuOTAzIDE0LjMzMiAxMDYuMTUyIDEzLjYwNjEgMTA2LjY1MSAxMy4wOTkxQzEwNy4xNDkgMTIuNTkyMSAxMDcuODczIDEyLjMzODYgMTA4LjgyMiAxMi4zMzg2QzEwOS40NjMgMTIuMzM4NiAxMDkuOTk4IDEyLjQ1MTMgMTEwLjQyNyAxMi42NzY2QzExMC44NTYgMTIuOTAyIDExMS4xNzcgMTMuMjIyNiAxMTEuMzg5IDEzLjYzODZDMTExLjYwNiAxNC4wNTAzIDExMS43MTQgMTQuNTQ0MyAxMTEuNzE0IDE1LjEyMDZWMTUuMzQ4MUMxMTEuNzE0IDE1LjUwNDEgMTExLjcwNSAxNS42NjQ1IDExMS42ODggMTUuODI5MUMxMTEuNjc1IDE1Ljk4OTUgMTExLjY1NiAxNi4xNDExIDExMS42MyAxNi4yODQxSDExMC4xMjJDMTEwLjEzNSAxNi4wNDU4IDExMC4xNDEgMTUuODIwNSAxMTAuMTQxIDE1LjYwODFDMTEwLjE0NSAxNS4zOTE1IDExMC4xNDggMTUuMTk2NSAxMTAuMTQ4IDE1LjAyMzFDMTEwLjE0OCAxNC43MjQxIDExMC4xIDE0LjQ3MDYgMTEwLjAwNSAxNC4yNjI2QzEwOS45MDkgMTQuMDUwMyAxMDkuNzY0IDEzLjg5IDEwOS41NjkgMTMuNzgxNkMxMDkuMzc0IDEzLjY3MzMgMTA5LjEyNSAxMy42MTkxIDEwOC44MjIgMTMuNjE5MUMxMDguMzc1IDEzLjYxOTEgMTA4LjA0NiAxMy43NDI2IDEwNy44MzQgMTMuOTg5NkMxMDcuNjIxIDE0LjIzNjYgMTA3LjUxNSAxNC41ODc2IDEwNy41MTUgMTUuMDQyNlYxNS42MzQxTDEwNy41MjIgMTUuODIyNlYxNi4zMjMxQzEwNy41MjIgMTYuNTIyNSAxMDcuNTUyIDE2LjcwNjYgMTA3LjYxMyAxNi44NzU2QzEwNy42NzggMTcuMDQ0NiAxMDcuNzg0IDE3LjE5MiAxMDcuOTMxIDE3LjMxNzZDMTA4LjA3OCAxNy40MzkgMTA4LjI3MyAxNy41MzQzIDEwOC41MTYgMTcuNjAzNkMxMDguNzYzIDE3LjY3MyAxMDkuMDcxIDE3LjcwNzYgMTA5LjQzOSAxNy43MDc2QzEwOS44MzggMTcuNzA3NiAxMTAuMjE3IDE3LjY2NDMgMTEwLjU3NyAxNy41Nzc2QzExMC45NDEgMTcuNDg2NiAxMTEuMjg1IDE3LjM2NTMgMTExLjYxIDE3LjIxMzZMMTExLjQ2NyAxOC41MjY2QzExMS4xNzcgMTguNjg3IDExMC44MzQgMTguODEyNiAxMTAuNDQgMTguOTAzNkMxMTAuMDUgMTguOTk5IDEwOS42MTcgMTkuMDQ2NiAxMDkuMTQgMTkuMDQ2NlpNMTA2Ljc4NyAxNi4yODQxVjE1LjE3OTFIMTExLjI5MlYxNi4yODQxSDEwNi43ODdaTTExNy4wMzggMTguODkwNlYxNS4wNjIxQzExNy4wMzggMTQuNzkzNSAxMTcuMDAxIDE0LjU2MTYgMTE2LjkyOCAxNC4zNjY2QzExNi44NTggMTQuMTcxNiAxMTYuNzQxIDE0LjAyIDExNi41NzcgMTMuOTExNkMxMTYuNDEyIDEzLjgwMzMgMTE2LjE4NyAxMy43NDkxIDExNS45MDEgMTMuNzQ5MUMxMTUuNjQ5IDEzLjc0OTEgMTE1LjQyOCAxMy43OTQ2IDExNS4yMzggMTMuODg1NkMxMTUuMDUxIDEzLjk3NjYgMTE0Ljg5OCAxNC4xMDAxIDExNC43NzYgMTQuMjU2MUMxMTQuNjU5IDE0LjQwNzggMTE0LjU3IDE0LjU4MTEgMTE0LjUxIDE0Ljc3NjFMMTE0LjI1IDEzLjg2NjFIMTE0LjU2MkMxMTQuNjMxIDEzLjU4NDUgMTE0Ljc0NiAxMy4zMzEgMTE0LjkwNiAxMy4xMDU2QzExNS4wNzEgMTIuODgwMyAxMTUuMjkyIDEyLjcwMjYgMTE1LjU2OSAxMi41NzI2QzExNS44NTEgMTIuNDM4MyAxMTYuMjAyIDEyLjM3MTEgMTE2LjYyMiAxMi4zNzExQzExNy4xMTIgMTIuMzcxMSAxMTcuNTA4IDEyLjQ2NDMgMTE3LjgxMiAxMi42NTA2QzExOC4xMTUgMTIuODMyNiAxMTguMzM4IDEzLjEwNTYgMTE4LjQ4MSAxMy40Njk2QzExOC42MjkgMTMuODMzNiAxMTguNzAyIDE0LjI4NDMgMTE4LjcwMiAxNC44MjE2VjE4Ljg5MDZIMTE3LjAzOFpNMTEyLjg4NSAxOC44OTA2VjEyLjUxNDFIMTE0LjU0OUwxMTQuNDg0IDE0LjA2NzZMMTE0LjU0OSAxNC4yMDQxVjE4Ljg5MDZIMTEyLjg4NVpNMTIyLjU0IDE5LjAyNzFDMTIxLjk5OSAxOS4wMjcxIDEyMS41NjUgMTguOTQ3IDEyMS4yNCAxOC43ODY2QzEyMC45MiAxOC42MjIgMTIwLjY4OCAxOC4zNzUgMTIwLjU0NSAxOC4wNDU2QzEyMC40MDIgMTcuNzE2MyAxMjAuMzMgMTcuMzExMSAxMjAuMzMgMTYuODMwMVYxMy4xMTIxSDEyMS45ODFWMTYuNTgzMUMxMjEuOTgxIDE2LjkyOTggMTIyLjA1OSAxNy4xODU1IDEyMi4yMTUgMTcuMzUwMUMxMjIuMzc2IDE3LjUxMDUgMTIyLjY1NSAxNy41OTA2IDEyMy4wNTQgMTcuNTkwNkMxMjMuMjg4IDE3LjU5MDYgMTIzLjUxMyAxNy41NjY4IDEyMy43MyAxNy41MTkxQzEyMy45NDYgMTcuNDY3MSAxMjQuMTQ2IDE3LjQgMTI0LjMyOCAxNy4zMTc2TDEyNC4xODUgMTguNzA4NkMxMjMuOTY4IDE4LjgwODMgMTIzLjcxOSAxOC44ODYzIDEyMy40MzcgMTguOTQyNkMxMjMuMTYgMTguOTk5IDEyMi44NjEgMTkuMDI3MSAxMjIuNTQgMTkuMDI3MVpNMTE5LjQwNyAxMy44NzkxVjEyLjU2NjFIMTI0LjI2OUwxMjQuMTI2IDEzLjg3OTFIMTE5LjQwN1pNMTIwLjM1IDEyLjY4OTZMMTIwLjM0MyAxMC45OTk2TDEyMi4wMDEgMTAuODMwNkwxMjEuOTM2IDEyLjY4OTZIMTIwLjM1Wk0xMzAuNzIzIDE5LjA2NjFDMTI5LjczNSAxOS4wNjYxIDEyOC45ODUgMTguODEyNiAxMjguNDc0IDE4LjMwNTZDMTI3Ljk2NyAxNy43OTg2IDEyNy43MTQgMTcuMDc3MSAxMjcuNzE0IDE2LjE0MTFWMTUuMjc2NkMxMjcuNzE0IDE0LjMzNjMgMTI3Ljk2NyAxMy42MTI2IDEyOC40NzQgMTMuMTA1NkMxMjguOTg1IDEyLjU5NDMgMTI5LjczNSAxMi4zMzg2IDEzMC43MjMgMTIuMzM4NkMxMzEuNzA3IDEyLjMzODYgMTMyLjQ1MiAxMi41OTQzIDEzMi45NTkgMTMuMTA1NkMxMzMuNDY2IDEzLjYxMjYgMTMzLjcyIDE0LjMzNjMgMTMzLjcyIDE1LjI3NjZWMTYuMTQxMUMxMzMuNzIgMTcuMDc3MSAxMzMuNDY2IDE3Ljc5ODYgMTMyLjk1OSAxOC4zMDU2QzEzMi40NTYgMTguODEyNiAxMzEuNzExIDE5LjA2NjEgMTMwLjcyMyAxOS4wNjYxWk0xMzAuNzIzIDE3LjczMzZDMTMxLjE1NiAxNy43MzM2IDEzMS40ODYgMTcuNjAzNiAxMzEuNzExIDE3LjM0MzZDMTMxLjk0MSAxNy4wODM2IDEzMi4wNTYgMTYuNzExIDEzMi4wNTYgMTYuMjI1NlYxNS4xOTIxQzEzMi4wNTYgMTQuNjk4MSAxMzEuOTQxIDE0LjMyMTEgMTMxLjcxMSAxNC4wNjExQzEzMS40ODYgMTMuNzk2OCAxMzEuMTU2IDEzLjY2NDYgMTMwLjcyMyAxMy42NjQ2QzEzMC4yODUgMTMuNjY0NiAxMjkuOTUyIDEzLjc5NjggMTI5LjcyMiAxNC4wNjExQzEyOS40OTcgMTQuMzIxMSAxMjkuMzg0IDE0LjY5ODEgMTI5LjM4NCAxNS4xOTIxVjE2LjIyNTZDMTI5LjM4NCAxNi43MTEgMTI5LjQ5NyAxNy4wODM2IDEyOS43MjIgMTcuMzQzNkMxMjkuOTUyIDE3LjYwMzYgMTMwLjI4NSAxNy43MzM2IDEzMC43MjMgMTcuNzMzNlpNMTM3LjE1NCAxMC4wOTYxQzEzNy40OTIgMTAuMDk2MSAxMzcuODA0IDEwLjEyNDMgMTM4LjA5IDEwLjE4MDZDMTM4LjM3NiAxMC4yMzcgMTM4LjYyOSAxMC4zMDYzIDEzOC44NSAxMC4zODg2TDEzOSAxMS42MzY2QzEzOC44MTMgMTEuNTgwMyAxMzguNjE4IDExLjUzNDggMTM4LjQxNSAxMS41MDAxQzEzOC4yMTUgMTEuNDYxMSAxMzcuOTk0IDExLjQ0MTYgMTM3Ljc1MiAxMS40NDE2QzEzNy40NjEgMTEuNDQxNiAxMzcuMjMyIDExLjQ3NDEgMTM3LjA2MyAxMS41MzkxQzEzNi44OTggMTEuNjA0MSAxMzYuNzgxIDExLjY5NzMgMTM2LjcxMiAxMS44MTg2QzEzNi42NDIgMTEuOTQgMTM2LjYwOCAxMi4wODMgMTM2LjYwOCAxMi4yNDc2VjEyLjI2NzFDMTM2LjYwOCAxMi4zODQxIDEzNi42MjUgMTIuNDk0NiAxMzYuNjYgMTIuNTk4NkMxMzYuNjk0IDEyLjcwMjYgMTM2LjczNSAxMi43OTU4IDEzNi43ODMgMTIuODc4MUwxMzUuNjk4IDEyLjkxNzFWMTIuNzQxNkMxMzUuNDk4IDEyLjY0NjMgMTM1LjMyOSAxMi41MDU1IDEzNS4xOTEgMTIuMzE5MUMxMzUuMDUyIDEyLjEzMjggMTM0Ljk4MyAxMS45MDEgMTM0Ljk4MyAxMS42MjM2VjExLjU5MTFDMTM0Ljk4MyAxMS4xMzE4IDEzNS4xNTggMTAuNzY3OCAxMzUuNTA5IDEwLjQ5OTFDMTM1Ljg2NCAxMC4yMzA1IDEzNi40MTMgMTAuMDk2MSAxMzcuMTU0IDEwLjA5NjFaTTEzNS4yMDQgMTguODkwNlYxMy4yNjE2SDEzNi44NjFWMTguODkwNkgxMzUuMjA0Wk0xMzQuMjgxIDE0LjEwNjZWMTIuNzg3MUwxMzUuODkzIDEyLjgwMDFMMTM2LjU0MyAxMi43ODcxSDEzOC45ODdMMTM4Ljg0NCAxNC4xMDY2SDEzNC4yODFaTTU2LjExNzUgMzEuODkwNkw1Ni4zOTA1IDIzLjU4MzZINTguODQ3NUw2MC40MDc1IDI4Ljg2ODFINjAuNDkyTDYyLjA0NTUgMjMuNTgzNkg2NC40OTZMNjQuNzc1NSAzMS44OTA2SDYzLjEyNDVMNjMuMDQ2NSAyOC45MDA2TDYyLjk3NSAyNS40NDkxSDYyLjg3MUw2MS4yMzk1IDMwLjkyMjFINTkuNjUzNUw1OC4wMTU1IDI1LjQ0OTFINTcuOTExNUw1Ny44NCAyOC45MDcxTDU3Ljc2ODUgMzEuODkwNkg1Ni4xMTc1Wk02OC45NjA0IDMyLjA2NjFDNjcuOTcyNCAzMi4wNjYxIDY3LjIyMjcgMzEuODEyNiA2Ni43MTE0IDMxLjMwNTZDNjYuMjA0NCAzMC43OTg2IDY1Ljk1MDkgMzAuMDc3MSA2NS45NTA5IDI5LjE0MTFWMjguMjc2NkM2NS45NTA5IDI3LjMzNjMgNjYuMjA0NCAyNi42MTI2IDY2LjcxMTQgMjYuMTA1NkM2Ny4yMjI3IDI1LjU5NDMgNjcuOTcyNCAyNS4zMzg2IDY4Ljk2MDQgMjUuMzM4NkM2OS45NDQgMjUuMzM4NiA3MC42ODk0IDI1LjU5NDMgNzEuMTk2NCAyNi4xMDU2QzcxLjcwMzQgMjYuNjEyNiA3MS45NTY5IDI3LjMzNjMgNzEuOTU2OSAyOC4yNzY2VjI5LjE0MTFDNzEuOTU2OSAzMC4wNzcxIDcxLjcwMzQgMzAuNzk4NiA3MS4xOTY0IDMxLjMwNTZDNzAuNjkzNyAzMS44MTI2IDY5Ljk0ODQgMzIuMDY2MSA2OC45NjA0IDMyLjA2NjFaTTY4Ljk2MDQgMzAuNzMzNkM2OS4zOTM3IDMwLjczMzYgNjkuNzIzIDMwLjYwMzYgNjkuOTQ4NCAzMC4zNDM2QzcwLjE3OCAzMC4wODM2IDcwLjI5MjkgMjkuNzExIDcwLjI5MjkgMjkuMjI1NlYyOC4xOTIxQzcwLjI5MjkgMjcuNjk4MSA3MC4xNzggMjcuMzIxMSA2OS45NDg0IDI3LjA2MTFDNjkuNzIzIDI2Ljc5NjggNjkuMzkzNyAyNi42NjQ2IDY4Ljk2MDQgMjYuNjY0NkM2OC41MjI3IDI2LjY2NDYgNjguMTg5IDI2Ljc5NjggNjcuOTU5NCAyNy4wNjExQzY3LjczNCAyNy4zMjExIDY3LjYyMTQgMjcuNjk4MSA2Ny42MjE0IDI4LjE5MjFWMjkuMjI1NkM2Ny42MjE0IDI5LjcxMSA2Ny43MzQgMzAuMDgzNiA2Ny45NTk0IDMwLjM0MzZDNjguMTg5IDMwLjYwMzYgNjguNTIyNyAzMC43MzM2IDY4Ljk2MDQgMzAuNzMzNlpNNzUuNjA1NyAzMi4wMjcxQzc1LjA2NCAzMi4wMjcxIDc0LjYzMDcgMzEuOTQ3IDc0LjMwNTcgMzEuNzg2NkM3My45ODUgMzEuNjIyIDczLjc1MzIgMzEuMzc1IDczLjYxMDIgMzEuMDQ1NkM3My40NjcyIDMwLjcxNjMgNzMuMzk1NyAzMC4zMTExIDczLjM5NTcgMjkuODMwMVYyNi4xMTIxSDc1LjA0NjdWMjkuNTgzMUM3NS4wNDY3IDI5LjkyOTggNzUuMTI0NyAzMC4xODU1IDc1LjI4MDcgMzAuMzUwMUM3NS40NDEgMzAuNTEwNSA3NS43MjA1IDMwLjU5MDYgNzYuMTE5MiAzMC41OTA2Qzc2LjM1MzIgMzAuNTkwNiA3Ni41Nzg1IDMwLjU2NjggNzYuNzk1MiAzMC41MTkxQzc3LjAxMTkgMzAuNDY3MSA3Ny4yMTEyIDMwLjQgNzcuMzkzMiAzMC4zMTc2TDc3LjI1MDIgMzEuNzA4NkM3Ny4wMzM1IDMxLjgwODMgNzYuNzg0NCAzMS44ODYzIDc2LjUwMjcgMzEuOTQyNkM3Ni4yMjU0IDMxLjk5OSA3NS45MjY0IDMyLjAyNzEgNzUuNjA1NyAzMi4wMjcxWk03Mi40NzI3IDI2Ljg3OTFWMjUuNTY2MUg3Ny4zMzQ3TDc3LjE5MTcgMjYuODc5MUg3Mi40NzI3Wk03My40MTUyIDI1LjY4OTZMNzMuNDA4NyAyMy45OTk2TDc1LjA2NjIgMjMuODMwNkw3NS4wMDEyIDI1LjY4OTZINzMuNDE1MlpNODEuMDA4MiAzMi4wNjYxQzgwLjAyMDIgMzIuMDY2MSA3OS4yNzA2IDMxLjgxMjYgNzguNzU5MiAzMS4zMDU2Qzc4LjI1MjIgMzAuNzk4NiA3Ny45OTg3IDMwLjA3NzEgNzcuOTk4NyAyOS4xNDExVjI4LjI3NjZDNzcuOTk4NyAyNy4zMzYzIDc4LjI1MjIgMjYuNjEyNiA3OC43NTkyIDI2LjEwNTZDNzkuMjcwNiAyNS41OTQzIDgwLjAyMDIgMjUuMzM4NiA4MS4wMDgyIDI1LjMzODZDODEuOTkxOSAyNS4zMzg2IDgyLjczNzIgMjUuNTk0MyA4My4yNDQyIDI2LjEwNTZDODMuNzUxMiAyNi42MTI2IDg0LjAwNDcgMjcuMzM2MyA4NC4wMDQ3IDI4LjI3NjZWMjkuMTQxMUM4NC4wMDQ3IDMwLjA3NzEgODMuNzUxMiAzMC43OTg2IDgzLjI0NDIgMzEuMzA1NkM4Mi43NDE2IDMxLjgxMjYgODEuOTk2MiAzMi4wNjYxIDgxLjAwODIgMzIuMDY2MVpNODEuMDA4MiAzMC43MzM2QzgxLjQ0MTYgMzAuNzMzNiA4MS43NzA5IDMwLjYwMzYgODEuOTk2MiAzMC4zNDM2QzgyLjIyNTkgMzAuMDgzNiA4Mi4zNDA3IDI5LjcxMSA4Mi4zNDA3IDI5LjIyNTZWMjguMTkyMUM4Mi4zNDA3IDI3LjY5ODEgODIuMjI1OSAyNy4zMjExIDgxLjk5NjIgMjcuMDYxMUM4MS43NzA5IDI2Ljc5NjggODEuNDQxNiAyNi42NjQ2IDgxLjAwODIgMjYuNjY0NkM4MC41NzA2IDI2LjY2NDYgODAuMjM2OSAyNi43OTY4IDgwLjAwNzIgMjcuMDYxMUM3OS43ODE5IDI3LjMyMTEgNzkuNjY5MiAyNy42OTgxIDc5LjY2OTIgMjguMTkyMVYyOS4yMjU2Qzc5LjY2OTIgMjkuNzExIDc5Ljc4MTkgMzAuMDgzNiA4MC4wMDcyIDMwLjM0MzZDODAuMjM2OSAzMC42MDM2IDgwLjU3MDYgMzAuNzMzNiA4MS4wMDgyIDMwLjczMzZaTTg2Ljg0MDIgMjguMTg1Nkw4Ni40MTc3IDI3LjA3NDFIODYuODIwN0M4Ni45Mzc3IDI2LjU1ODUgODcuMTUgMjYuMTUzMyA4Ny40NTc3IDI1Ljg1ODZDODcuNzY1MyAyNS41NjQgODguMTkyMiAyNS40MTY2IDg4LjczODIgMjUuNDE2NkM4OC44NTA4IDI1LjQxNjYgODguOTUyNyAyNS40MjUzIDg5LjA0MzcgMjUuNDQyNkM4OS4xMzQ3IDI1LjQ1NTYgODkuMjE3IDI1LjQ3MyA4OS4yOTA3IDI1LjQ5NDZMODkuMzgxNyAyNy4xNTg2Qzg5LjI4NjMgMjcuMTI4MyA4OS4xNzU4IDI3LjEwNjYgODkuMDUwMiAyNy4wOTM2Qzg4LjkyNDUgMjcuMDc2MyA4OC43OTIzIDI3LjA2NzYgODguNjUzNyAyNy4wNjc2Qzg4LjIxMTcgMjcuMDY3NiA4Ny44MzQ3IDI3LjE2NTEgODcuNTIyNyAyNy4zNjAxQzg3LjIxNSAyNy41NTUxIDg2Ljk4NzUgMjcuODMwMyA4Ni44NDAyIDI4LjE4NTZaTTg1LjIyMTcgMzEuODkwNlYyNS41MTQxSDg2LjgwNzdMODYuNzM2MiAyNy40NzA2TDg2Ljg4NTcgMjcuNTI5MVYzMS44OTA2SDg1LjIyMTdaTTk0Ljc2NzIgMzEuODkwNkw5Mi40MDc3IDIzLjU4MzZIOTQuMTc1N0w5NS45OTU3IDMwLjYxNjZIOTYuMTQ1Mkw5Ny45NjUyIDIzLjU4MzZIOTkuNzMzMkw5Ny4zODAyIDMxLjg5MDZIOTQuNzY3MlpNMTAzLjA0NiAzMi4wNDY2QzEwMS45NTQgMzIuMDQ2NiAxMDEuMTQyIDMxLjc5NTMgMTAwLjYwOSAzMS4yOTI2QzEwMC4wNzYgMzAuNzkgOTkuODA5MyAzMC4wNjIgOTkuODA5MyAyOS4xMDg2VjI4LjI3NjZDOTkuODA5MyAyNy4zMzIgMTAwLjA1OCAyNi42MDYxIDEwMC41NTcgMjYuMDk5MUMxMDEuMDU1IDI1LjU5MjEgMTAxLjc3OSAyNS4zMzg2IDEwMi43MjggMjUuMzM4NkMxMDMuMzY5IDI1LjMzODYgMTAzLjkwNCAyNS40NTEzIDEwNC4zMzMgMjUuNjc2NkMxMDQuNzYyIDI1LjkwMiAxMDUuMDgzIDI2LjIyMjYgMTA1LjI5NSAyNi42Mzg2QzEwNS41MTIgMjcuMDUwMyAxMDUuNjIgMjcuNTQ0MyAxMDUuNjIgMjguMTIwNlYyOC4zNDgxQzEwNS42MiAyOC41MDQxIDEwNS42MTIgMjguNjY0NSAxMDUuNTk0IDI4LjgyOTFDMTA1LjU4MSAyOC45ODk1IDEwNS41NjIgMjkuMTQxMSAxMDUuNTM2IDI5LjI4NDFIMTA0LjAyOEMxMDQuMDQxIDI5LjA0NTggMTA0LjA0NyAyOC44MjA1IDEwNC4wNDcgMjguNjA4MUMxMDQuMDUyIDI4LjM5MTUgMTA0LjA1NCAyOC4xOTY1IDEwNC4wNTQgMjguMDIzMUMxMDQuMDU0IDI3LjcyNDEgMTA0LjAwNiAyNy40NzA2IDEwMy45MTEgMjcuMjYyNkMxMDMuODE1IDI3LjA1MDMgMTAzLjY3IDI2Ljg5IDEwMy40NzUgMjYuNzgxNkMxMDMuMjggMjYuNjczMyAxMDMuMDMxIDI2LjYxOTEgMTAyLjcyOCAyNi42MTkxQzEwMi4yODEgMjYuNjE5MSAxMDEuOTUyIDI2Ljc0MjYgMTAxLjc0IDI2Ljk4OTZDMTAxLjUyNyAyNy4yMzY2IDEwMS40MjEgMjcuNTg3NiAxMDEuNDIxIDI4LjA0MjZWMjguNjM0MUwxMDEuNDI4IDI4LjgyMjZWMjkuMzIzMUMxMDEuNDI4IDI5LjUyMjUgMTAxLjQ1OCAyOS43MDY2IDEwMS41MTkgMjkuODc1NkMxMDEuNTg0IDMwLjA0NDYgMTAxLjY5IDMwLjE5MiAxMDEuODM3IDMwLjMxNzZDMTAxLjk4NSAzMC40MzkgMTAyLjE4IDMwLjUzNDMgMTAyLjQyMiAzMC42MDM2QzEwMi42NjkgMzAuNjczIDEwMi45NzcgMzAuNzA3NiAxMDMuMzQ1IDMwLjcwNzZDMTAzLjc0NCAzMC43MDc2IDEwNC4xMjMgMzAuNjY0MyAxMDQuNDgzIDMwLjU3NzZDMTA0Ljg0NyAzMC40ODY2IDEwNS4xOTEgMzAuMzY1MyAxMDUuNTE2IDMwLjIxMzZMMTA1LjM3MyAzMS41MjY2QzEwNS4wODMgMzEuNjg3IDEwNC43NDEgMzEuODEyNiAxMDQuMzQ2IDMxLjkwMzZDMTAzLjk1NiAzMS45OTkgMTAzLjUyMyAzMi4wNDY2IDEwMy4wNDYgMzIuMDQ2NlpNMTAwLjY5MyAyOS4yODQxVjI4LjE3OTFIMTA1LjE5OFYyOS4yODQxSDEwMC42OTNaTTExMC45NDQgMzEuODkwNlYyOC4wNjIxQzExMC45NDQgMjcuNzkzNSAxMTAuOTA4IDI3LjU2MTYgMTEwLjgzNCAyNy4zNjY2QzExMC43NjUgMjcuMTcxNiAxMTAuNjQ4IDI3LjAyIDExMC40ODMgMjYuOTExNkMxMTAuMzE4IDI2LjgwMzMgMTEwLjA5MyAyNi43NDkxIDEwOS44MDcgMjYuNzQ5MUMxMDkuNTU2IDI2Ljc0OTEgMTA5LjMzNyAyNi43OTQ2IDEwOS4xNSAyNi44ODU2QzEwOC45NjQgMjYuOTc2NiAxMDguODEgMjcuMTAwMSAxMDguNjg5IDI3LjI1NjFDMTA4LjU3MiAyNy40MDc4IDEwOC40ODUgMjcuNTgxMSAxMDguNDI5IDI3Ljc3NjFMMTA4LjA5MSAyNi44NjYxSDEwOC40ODdDMTA4LjU2MSAyNi41ODQ1IDEwOC42NzggMjYuMzMxIDEwOC44MzggMjYuMTA1NkMxMDguOTk5IDI1Ljg4MDMgMTA5LjIxOCAyNS43MDI2IDEwOS40OTUgMjUuNTcyNkMxMDkuNzcyIDI1LjQzODMgMTEwLjExNyAyNS4zNzExIDExMC41MjggMjUuMzcxMUMxMTEuMDE4IDI1LjM3MTEgMTExLjQxNSAyNS40NjQzIDExMS43MTggMjUuNjUwNkMxMTIuMDIxIDI1LjgzMjYgMTEyLjI0NCAyNi4xMDU2IDExMi4zODcgMjYuNDY5NkMxMTIuNTM1IDI2LjgzMzYgMTEyLjYwOCAyNy4yODQzIDExMi42MDggMjcuODIxNlYzMS44OTA2SDExMC45NDRaTTEwNi43OTEgMzEuODkwNlYyMy4yOTc2SDEwOC40NDhWMjUuMjYwNkwxMDguNDE2IDI3LjI0MzFMMTA4LjQ1NSAyNy4zNjY2VjMxLjg5MDZIMTA2Ljc5MVpNMTE0LjAxNSAzMS44OTA2VjI1LjUxNDFIMTE1LjY3OVYzMS44OTA2SDExNC4wMTVaTTExNC44NDcgMjQuNzYwMUMxMTQuNTMxIDI0Ljc2MDEgMTE0LjI5NyAyNC42ODY1IDExNC4xNDUgMjQuNTM5MUMxMTMuOTk4IDI0LjM4NzUgMTEzLjkyNCAyNC4xNzk1IDExMy45MjQgMjMuOTE1MVYyMy44ODI2QzExMy45MjQgMjMuNjE4MyAxMTMuOTk4IDIzLjQxMDMgMTE0LjE0NSAyMy4yNTg2QzExNC4yOTcgMjMuMTA3IDExNC41MzEgMjMuMDMxMSAxMTQuODQ3IDIzLjAzMTFDMTE1LjE1OSAyMy4wMzExIDExNS4zOTEgMjMuMTA3IDExNS41NDIgMjMuMjU4NkMxMTUuNjk0IDIzLjQxMDMgMTE1Ljc3IDIzLjYxODMgMTE1Ljc3IDIzLjg4MjZWMjMuOTE1MUMxMTUuNzcgMjQuMTgzOCAxMTUuNjk0IDI0LjM5MTggMTE1LjU0MiAyNC41MzkxQzExNS4zOTEgMjQuNjg2NSAxMTUuMTU5IDI0Ljc2MDEgMTE0Ljg0NyAyNC43NjAxWk0xMTkuNzk2IDMyLjA1MzFDMTE4LjgxMyAzMi4wNTMxIDExOC4wODIgMzEuNzkzMSAxMTcuNjA2IDMxLjI3MzFDMTE3LjEzMyAzMC43NTMxIDExNi44OTcgMzAuMDIzIDExNi44OTcgMjkuMDgyNlYyOC4zMDI2QzExNi44OTcgMjcuMzY2NiAxMTcuMTM2IDI2LjY0MDggMTE3LjYxMiAyNi4xMjUxQzExOC4wODkgMjUuNjA5NSAxMTguODE3IDI1LjM1MTYgMTE5Ljc5NiAyNS4zNTE2QzEyMC4wNTIgMjUuMzUxNiAxMjAuMjkgMjUuMzczMyAxMjAuNTExIDI1LjQxNjZDMTIwLjczNyAyNS40NTU2IDEyMC45NCAyNS41MDk4IDEyMS4xMjIgMjUuNTc5MUMxMjEuMzA5IDI1LjY0ODUgMTIxLjQ3MSAyNS43MjIxIDEyMS42MSAyNS44MDAxTDEyMS43NDYgMjcuMTk3NkMxMjEuNTM0IDI3LjA2MzMgMTIxLjI5NiAyNi45NTA2IDEyMS4wMzEgMjYuODU5NkMxMjAuNzcxIDI2Ljc2ODYgMTIwLjQ3IDI2LjcyMzEgMTIwLjEyOCAyNi43MjMxQzExOS41OSAyNi43MjMxIDExOS4xOTYgMjYuODYxOCAxMTguOTQ1IDI3LjEzOTFDMTE4LjY5OCAyNy40MTIxIDExOC41NzQgMjcuODEwOCAxMTguNTc0IDI4LjMzNTFWMjkuMDI0MUMxMTguNTc0IDI5LjU0NDEgMTE4LjcwMiAyOS45NDUgMTE4Ljk1OCAzMC4yMjY2QzExOS4yMTggMzAuNTA4MyAxMTkuNjE2IDMwLjY0OTEgMTIwLjE1NCAzMC42NDkxQzEyMC40OTYgMzAuNjQ5MSAxMjAuNzk5IDMwLjYwNTggMTIxLjA2NCAzMC41MTkxQzEyMS4zMjggMzAuNDI4MSAxMjEuNTc1IDMwLjMxNzYgMTIxLjgwNSAzMC4xODc2TDEyMS42NjggMzEuNTkxNkMxMjEuNDU2IDMxLjcxMyAxMjEuMTg5IDMxLjgxOTEgMTIwLjg2OSAzMS45MTAxQzEyMC41NDggMzIuMDA1NSAxMjAuMTkxIDMyLjA1MzEgMTE5Ljc5NiAzMi4wNTMxWk0xMjIuOTAyIDMxLjg5MDZWMjMuMjk3NkgxMjQuNTcyVjMxLjg5MDZIMTIyLjkwMlpNMTI5LjAzNCAzMi4wNDY2QzEyNy45NDIgMzIuMDQ2NiAxMjcuMTI5IDMxLjc5NTMgMTI2LjU5NiAzMS4yOTI2QzEyNi4wNjMgMzAuNzkgMTI1Ljc5NyAzMC4wNjIgMTI1Ljc5NyAyOS4xMDg2VjI4LjI3NjZDMTI1Ljc5NyAyNy4zMzIgMTI2LjA0NiAyNi42MDYxIDEyNi41NDQgMjYuMDk5MUMxMjcuMDQyIDI1LjU5MjEgMTI3Ljc2NiAyNS4zMzg2IDEyOC43MTUgMjUuMzM4NkMxMjkuMzU2IDI1LjMzODYgMTI5Ljg5MiAyNS40NTEzIDEzMC4zMjEgMjUuNjc2NkMxMzAuNzUgMjUuOTAyIDEzMS4wNyAyNi4yMjI2IDEzMS4yODMgMjYuNjM4NkMxMzEuNDk5IDI3LjA1MDMgMTMxLjYwOCAyNy41NDQzIDEzMS42MDggMjguMTIwNlYyOC4zNDgxQzEzMS42MDggMjguNTA0MSAxMzEuNTk5IDI4LjY2NDUgMTMxLjU4MiAyOC44MjkxQzEzMS41NjkgMjguOTg5NSAxMzEuNTQ5IDI5LjE0MTEgMTMxLjUyMyAyOS4yODQxSDEzMC4wMTVDMTMwLjAyOCAyOS4wNDU4IDEzMC4wMzUgMjguODIwNSAxMzAuMDM1IDI4LjYwODFDMTMwLjAzOSAyOC4zOTE1IDEzMC4wNDEgMjguMTk2NSAxMzAuMDQxIDI4LjAyMzFDMTMwLjA0MSAyNy43MjQxIDEyOS45OTMgMjcuNDcwNiAxMjkuODk4IDI3LjI2MjZDMTI5LjgwMyAyNy4wNTAzIDEyOS42NTggMjYuODkgMTI5LjQ2MyAyNi43ODE2QzEyOS4yNjggMjYuNjczMyAxMjkuMDE4IDI2LjYxOTEgMTI4LjcxNSAyNi42MTkxQzEyOC4yNjkgMjYuNjE5MSAxMjcuOTM5IDI2Ljc0MjYgMTI3LjcyNyAyNi45ODk2QzEyNy41MTUgMjcuMjM2NiAxMjcuNDA5IDI3LjU4NzYgMTI3LjQwOSAyOC4wNDI2VjI4LjYzNDFMMTI3LjQxNSAyOC44MjI2VjI5LjMyMzFDMTI3LjQxNSAyOS41MjI1IDEyNy40NDUgMjkuNzA2NiAxMjcuNTA2IDI5Ljg3NTZDMTI3LjU3MSAzMC4wNDQ2IDEyNy42NzcgMzAuMTkyIDEyNy44MjUgMzAuMzE3NkMxMjcuOTcyIDMwLjQzOSAxMjguMTY3IDMwLjUzNDMgMTI4LjQxIDMwLjYwMzZDMTI4LjY1NyAzMC42NzMgMTI4Ljk2NCAzMC43MDc2IDEyOS4zMzMgMzAuNzA3NkMxMjkuNzMxIDMwLjcwNzYgMTMwLjExIDMwLjY2NDMgMTMwLjQ3IDMwLjU3NzZDMTMwLjgzNCAzMC40ODY2IDEzMS4xNzkgMzAuMzY1MyAxMzEuNTA0IDMwLjIxMzZMMTMxLjM2MSAzMS41MjY2QzEzMS4wNyAzMS42ODcgMTMwLjcyOCAzMS44MTI2IDEzMC4zMzQgMzEuOTAzNkMxMjkuOTQ0IDMxLjk5OSAxMjkuNTEgMzIuMDQ2NiAxMjkuMDM0IDMyLjA0NjZaTTEyNi42ODEgMjkuMjg0MVYyOC4xNzkxSDEzMS4xODVWMjkuMjg0MUgxMjYuNjgxWk0xMzQuOTk0IDMyLjA0NjZDMTM0LjQ4MyAzMi4wNDY2IDEzNC4wMjggMzEuOTk2OCAxMzMuNjI5IDMxLjg5NzFDMTMzLjIzNSAzMS44MDE4IDEzMi44OTkgMzEuNjkzNSAxMzIuNjIxIDMxLjU3MjFMMTMyLjQ3MiAzMC4xMjkxQzEzMi44MDEgMzAuMjgwOCAxMzMuMTYzIDMwLjQxMyAxMzMuNTU3IDMwLjUyNTZDMTMzLjk1NiAzMC42MzgzIDEzNC4zOTQgMzAuNjk0NiAxMzQuODcgMzAuNjk0NkMxMzUuMjg2IDMwLjY5NDYgMTM1LjU4OCAzMC42NDI2IDEzNS43NzQgMzAuNTM4NkMxMzUuOTYgMzAuNDMwMyAxMzYuMDUzIDMwLjI3IDEzNi4wNTMgMzAuMDU3NlYzMC4wMTg2QzEzNi4wNTMgMjkuODc1NiAxMzYuMDEgMjkuNzU4NiAxMzUuOTIzIDI5LjY2NzZDMTM1Ljg0MSAyOS41NzY2IDEzNS42OTQgMjkuNDk0MyAxMzUuNDgxIDI5LjQyMDZDMTM1LjI2OSAyOS4zNDI2IDEzNC45NyAyOS4yNjAzIDEzNC41ODQgMjkuMTczNkMxMzQuMDUxIDI5LjA0OCAxMzMuNjI5IDI4LjkwMjggMTMzLjMxNyAyOC43MzgxQzEzMy4wMDkgMjguNTY5MSAxMzIuNzg4IDI4LjM2MzMgMTMyLjY1NCAyOC4xMjA2QzEzMi41MiAyNy44NzM2IDEzMi40NTIgMjcuNTc5IDEzMi40NTIgMjcuMjM2NlYyNy4xNzgxQzEzMi40NTIgMjYuNTc1OCAxMzIuNjY3IDI2LjEyMyAxMzMuMDk2IDI1LjgxOTZDMTMzLjUyNSAyNS41MTIgMTM0LjE2IDI1LjM1ODEgMTM1IDI1LjM1ODFDMTM1LjQ5OSAyNS4zNTgxIDEzNS45MzkgMjUuNDA4IDEzNi4zMiAyNS41MDc2QzEzNi43MDYgMjUuNjAzIDEzNy4wMjYgMjUuNzE3OCAxMzcuMjgyIDI1Ljg1MjFMMTM3LjQzMSAyNy4xNzgxQzEzNy4xMjggMjcuMDM1MSAxMzYuNzg4IDI2LjkxNiAxMzYuNDExIDI2LjgyMDZDMTM2LjAzNCAyNi43MjEgMTM1LjYyOSAyNi42NzExIDEzNS4xOTUgMjYuNjcxMUMxMzQuOTE0IDI2LjY3MTEgMTM0LjY5MSAyNi42OTUgMTM0LjUyNiAyNi43NDI2QzEzNC4zNjYgMjYuNzg2IDEzNC4yNTEgMjYuODQ4OCAxMzQuMTgxIDI2LjkzMTFDMTM0LjExMiAyNy4wMTM1IDEzNC4wNzcgMjcuMTEzMSAxMzQuMDc3IDI3LjIzMDFWMjcuMjYyNkMxMzQuMDc3IDI3LjM5MjYgMTM0LjExNCAyNy41MDUzIDEzNC4xODggMjcuNjAwNkMxMzQuMjY2IDI3LjY5NiAxMzQuNDA3IDI3Ljc4MjYgMTM0LjYxIDI3Ljg2MDZDMTM0LjgxNCAyNy45MzQzIDEzNS4xIDI4LjAxNDUgMTM1LjQ2OCAyOC4xMDExQzEzNi4wMDYgMjguMjEzOCAxMzYuNDM3IDI4LjM0NiAxMzYuNzYyIDI4LjQ5NzZDMTM3LjA4NyAyOC42NDkzIDEzNy4zMjMgMjguODQ0MyAxMzcuNDcgMjkuMDgyNkMxMzcuNjE4IDI5LjMxNjYgMTM3LjY5MSAyOS42MjQzIDEzNy42OTEgMzAuMDA1NlYzMC4wODM2QzEzNy42OTEgMzAuNzQyMyAxMzcuNDY4IDMxLjIzNDEgMTM3LjAyMiAzMS41NTkxQzEzNi41NzYgMzEuODg0MSAxMzUuOSAzMi4wNDY2IDEzNC45OTQgMzIuMDQ2NloiIGZpbGw9IiNGNkY2RjYiLz4KPC9zdmc+ClhAybDn2tK8ED02UtdSJzxV+NF3tMuGQLcvndUGObrYgXP9y2NBNV6vVsNmkNt4zDnadCx8uyAr93vIuAsBRMiUkA=="
}
}
```
* `id` : Unique identifier of this credential.
* `encoded` : Encoded version of the credential, represented as a base64 string.
* `decoded` : Decoded version of the credential:
* `namespaces` : Each namespace corresponds to a group of claims included in the credential.
These can be claims that are part of a specific standard, jurisdiction or any other
reference.
* `org.iso.18013.5.1` : This namespace includes claims that comply with the
[ISO/IEC 18013-5 standard](https://www.iso.org/standard/69084.html). Each element in
this object represents a claim and includes the following fields:
* `digestID` : Serial number identifying the claim within this namespace.
* `random` : This is the random value (nonce/salt) added to each claim when it is
encoded. This enables
[unwanted disclosures protection](#salt-values-support-unwanted-disclosures-protection).
* `elementIdentifier` : The identifier of this claim as per
[ISO/IEC 18013-5 standard](https://www.iso.org/standard/69084.html).
* `elementValue` :
* `type` : Claim data type.
* `value` : Claim value.
* `issuerAuth` : This is the digital signature represented as a COSE\_sign1 structure. It
includes different details regarding the encrypton algorithm, certificates and public
keys.
## Branded mDoc [#branded-mdoc]
The following image depicts how the credential above would look like in the holder's digital wallet:
# MATTR GO Changelog
URL: /docs/resources/changelog/go
[Subscribe](/docs/resources/changelog/newsletter-signup) to our release
newsletter to get the latest updates on product releases, new features and bug
fixes.
## Default engagement method and verification improvements in MATTR GO Verify
2026-06-22
Tags: GO Verify
This release (v1.9.0) of the
[MATTR GO Verify example app](/docs/verification/go-verify/getting-started) lets you choose how
verifications start and includes a range of fixes and improvements:
* **Choose your default engagement method**: You can now set the default method used to start a
verification, either QR code or NFC. This setting applies only to devices that support NFC, such
as supported Android devices. If you do not set a default, NFC-capable devices use NFC by default
while all other devices continue to use QR code engagement.
* **Verification template icon fix**: We have fixed an issue on the verification template details
screen where a placeholder icon could appear instead of the icon configured for the template. The
screen now shows the correct icon for each verification template.
* **Clearer text for hidden claims**: We have improved the default message shown when claims
returned by the holder are hidden after a failed verification, so it more clearly explains why
those claims are not displayed.
* **Logging improvements**: We've continued to improve the internal logging functionality of our
app, making it easier to debug if required.
The MATTR GO Verify example app can be branded and configured to meet all your production
verification needs. Check out our
[MATTR GO Verify onboarding guide](/docs/verification/go-verify/getting-started) for more
information.
## Verification checks, expected values, and approved purposes in MATTR GO Verify
2026-06-09
Tags: GO Verify
This release (v1.8.0) of the
[MATTR GO Verify example app](/docs/verification/go-verify/getting-started) introduces new ways to
define what a successful verification means and a refreshed experience to match:
* **Use cases are now verification checks**: Use cases have been renamed to verification checks to
better reflect how they support real-world credential verification. A verification check can now
assess whether a credential is both valid and acceptable for a specific purpose, helping users
make clearer decisions based on your requirements.
* **Expected values**: You can now configure expected values within verification check templates to
define the exact claim values required for a successful outcome, such as confirming that an
`is_over_18` claim is set to `true`.
* **Approved purposes**: Approved purposes give you greater control over which issuers are
acceptable for a verification check. When enabled, only credentials issued by approved issuers for
that purpose are accepted, even if credentials from other issuers are otherwise valid.
* **Refreshed home and result screens**: The home screen now groups a check that can be satisfied
with multiple doc types into a single entry, with users able to select supported doc types
from within the check. Result screens have been updated to better reflect both credential validity
and acceptability, with clearer messaging, improved error visibility, and more control over
whether claim values are shown on invalid result screens. When expected values or approved
purposes are used, results are simplified to Approved or Not approved, with intermediate states
such as unable to verify, revoked, or not yet active treated as not approved for a clearer outcome.
* **Customizable result messaging**: Verification checks that use expected values or approved
purposes can now display custom result headings, configured through your language pack. Each
verification check can also include its own success and failure hint text to provide contextual
guidance after a result, while maintaining backwards compatibility when no hint text is
configured.
The MATTR GO Verify example app can be branded and configured to meet all your production
verification needs. Check out our
[MATTR GO Verify onboarding guide](/docs/verification/go-verify/getting-started) for more
information.
## Return to wallet
2026-04-30
Tags: GO Hold
Version 3.8.0 of the [MATTR GO Hold example app](/docs/holding/go-hold/getting-started) is now available in the app stores.
Based on feedback, we have removed the "Return to wallet" button during same-device presentations.
This change reduces confusion and helps prevent users from accidentally exiting the flow before completing their presentation. The result is a more streamlined and reliable user experience.
**Bug fixes and under-the-hood improvements**
We have also made several improvements to enhance stability, security, and the overall user experience, including:
* Fixes an issue that caused initialization issues on iOS devices as a result of app pre-warming.
* Fixes to auto-delete settings to ensure expected behaviour.
* Fixes for broken links within the app.
* Further enhancements to how on-device logs are generated and secured.
* The iOS app is now being built using iOS 26 SDK, aligning with Apple's latest App Store Connect. This has no effect on the minimum version required to run the app (iOS 15 or higher).
## Status List support update in GO Verify
2026-04-13
Tags: GO Verify
This release (v1.7.0) of the [MATTR GO Verify example app](/docs/verification/go-verify/getting-started) adds support for a newer version of the Token Status List specification:
* **Token Status List Draft 14 support**: The app can now check the revocation status of credentials issued using the [Token Status List Draft 14](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-status-list-14) specification while maintaining existing support for credentials issued using Draft 3.
## Stability and visual improvements in GO Verify
2026-02-27
Tags: GO Verify
This release (v1.6.0) of the [MATTR GO Verify example app](/docs/verification/go-verify/getting-started) introduces improvements to stability and user interface consistency:
* **NFC stability improvements**: Changes have been made to improve general stability, especially when using NFC to request a credential.
* **Improved visual consistency**: We have fixed a number of visual inconsistencies on the Privacy Policy, Terms of Use, Settings and Trusted Issuer pages.
## Improved notifications and debugging in GO Hold
2026-02-23
Tags: GO Hold
Version 3.7.0 of the [MATTR GO Hold example app](/docs/holding/go-hold/getting-started) is now available in the app stores.
This release improves the reliability of push notifications and how the app responds when you interact with them. Tapping a notification when the app is closed will now more consistently take you to the relevant screen, rather than simply opening the app.
## Camera performance improvements in GO Verify
2025-12-09
Tags: GO Verify
This release (v1.5.1) of the [MATTR GO Verify example app](/docs/verification/go-verify/getting-started) introduces focused improvements to camera performance and reliability:
* **Faster camera startup**: Under-the-hood optimizations have significantly reduced camera initialization time, ensuring verification workflows begin more quickly.
* **More responsive camera switching**: Switching between front and rear cameras now happens more smoothly, reducing delays during verification.
* **Improved torch reliability**: The torch toggle now operates more consistently, automatically turning off when the app moves to the background or when switching from rear to front camera.
These enhancements ensure a more reliable and responsive verification experience across different lighting conditions and use cases.
## Enhanced user experience and performance in GO Hold
2025-11-25
Tags: GO Hold
Version 3.6.0 of the [MATTR GO Hold example app](/docs/holding/go-hold/getting-started) is now available in the app stores. This release introduces streamlined navigation, improved credential claiming, and foundational performance enhancements.
Highlights include:
* **Simplified post-claim navigation**: After claiming a credential, users are now taken directly to the Wallet screen, where the new credential appears. This creates a more intuitive flow and helps users quickly access their newly issued credentials.
* **Seamless claim experience**: Users can now start the credential issuance flow from outside the app by scanning a QR code with their device's native camera or following deep links. This eliminates friction and makes it easier to begin the credential claiming journey.
* **Architecture and performance improvements**: This release includes foundational updates to enhance stability, compatibility, and overall app performance, ensuring a reliable experience across devices.
## MATTR GO Verify 1.5.0
2025-11-14
Tags: GO Verify
This release (v1.5.0) of the [MATTR GO Verify example app](/docs/verification/go-verify/getting-started) introduces several enhancements designed to make verifying digital credentials clearer, faster, and more intuitive:
* **Refined verification results**: Credential details now take center stage, with the portrait image displayed prominently for improved visual context.
* **Improved readability**: The Privacy Statement and Terms of Use now feature responsive text sizing, ensuring a seamless reading experience across all devices.
* **React Native architecture upgrade**: Enabled the new React Native architecture to support enhanced performance and prepare the foundation for future capabilities.
* **Bug fixes and enhancements**: Various bug fixes and improvements to ensure continued reliability and optimal performance.
## Improved handling of untrusted issuers in MATTR GO Verify
2025-10-01
Tags: GO Verify
This release (v1.4.0) of the
[MATTR GO Verify example app](/docs/verification/go-verify/getting-started) introduces
improved handling of credentials from untrusted issuers, along with infrastructure upgrades and
various bug fixes and user experience improvements.
* **Improved handling of untrusted issuers**: MATTR GO Verify now shows an explicit warning if the
issuer of a credential presented for verification has certificates that are unknown, expired, or
not yet valid. In these cases, all claims are blurred by default and an explicit warning is shown.
Users can still review the claims by tapping “Show Claims” within the warning.
This change helps protect against accidental exposure to potentially fraudulent credentials,
while ensuring claims remain accessible for informed review when needed.
* **Minor fixes and UX improvements**: Various bug fixes and user experience enhancements ensure
smoother operation across verification workflows.
## Enhanced issuance and performance in GO Hold
2025-09-12
Tags: GO Hold
Version 3.5.0 of the [MATTR GO Hold example app](/docs/holding/go-hold/getting-started) is now available in the app stores. This release introduces updates and enhancements designed to support smooth and flexible digital credential experiences.
Highlights include:
* **Support for OID4VCI Pre-Authorized Code flow**: Introducing support for the [OID4VCI Pre-authorized Code flow](/docs/issuance/pre-authorized-code/overview), holders can now receive credentials directly from issuers without needing to authenticate at the moment of issuance. This enables simpler, low-friction credential journeys while maintaining strong security.
* **Infrastructure upgrades**: Improvements under the hood provide better app performance today and strengthen the foundations for future features.
* **General bug fixes and stability improvements**: Ongoing updates ensure the app continues to run reliably in production environments.
These improvements further streamline the user experience and make it easier for organizations to deliver trusted digital credential journeys at scale.
## Smarter verification, greater flexibility in GO Verify
2025-07-01
Tags: GO Verify
This release (v1.3.1) of the
[MATTR GO Verify example app](/docs/verification/go-verify/getting-started) introduces new
features that improve verification flexibility and clarity:
* Dynamic verification presets: GO Verify can now retrieve the latest verification presets when
online, then cache them for offline use.
* Dynamic trusted issuer list: Trusted issuer information is now dynamically updatable, removing the
need for app releases when changes are needed. This allows for faster trust list management and
greater responsiveness to trust network updates.
* Clearer verification results: We've updated the copy on the results screen to show both the
credential’s status and a summary of the verification result, enhancing transparency and helping
users confidently interpret outcomes.
* NFC support for Android: GO Verify now supports NFC device engagement for mDocs in-person
verification on Android devices. BLE continues to handle data transfer, while NFC enables
intuitive tap-to-verify experiences.
The MATTR GO Verify example app can be branded and configured to meet all your production
verification needs. Check out our
[MATTR GO Verify onboarding guide](/docs/verification/go-verify/getting-started) for more
information.
## Improved credential status messages in GO Hold
2025-06-30
Tags: GO Hold
Version 3.4.0 of the [MATTR GO Hold example app](/docs/holding/go-hold/getting-started) is
now available in the app stores. This release introduces improved credential status messaging to
enhance user clarity and trust, along with general stability and security improvements.
Users will now see clearer, more informative messages when a credential is:
* Temporarily or permanently revoked
* Issued by an untrusted source
* Expired or not yet active
* Of an unsupported type
These updates help users quickly understand the status of their credentials and take appropriate
action.
This version also includes various bug fixes and security enhancements to ensure a smoother and more
secure experience.
## mDocs revocation support in GO Hold
2025-03-20
Tags: GO Hold
Version 3.3.1 of the [MATTR GO Hold example app](/docs/holding/go-hold/getting-started) is
now live in the app stores. This release introduces support for
[mDocs revocation](/docs/issuance/revocation/overview), enabling the app to perform status checks on any claimed
credentials. Users can now easily view the status of their mDocs and see whether they are active or
have been revoked, providing full transparency and control over their credentials.
The MATTR GO Hold example app can be branded and configured to meet all your production credential
holding needs. Check out our
[MATTR GO Hold onboarding guide](/docs/holding/go-hold/getting-started) for more
information.
## mDocs revocation support in GO Verify
2025-03-12
Tags: GO Verify
This release (v1.2.0) of the
[MATTR GO Verify example app](/docs/verification/go-verify/getting-started) introduces support
for [mDocs revocation](/docs/issuance/revocation/overview). You can configure the app to perform mDocs status
checks as part of the verification workflow, ensuring revoked mDocs are not verified.
The MATTR GO Verify example app can be branded and configured to meet all your production
verification needs. Check out our
[MATTR GO Verify onboarding guide](/docs/verification/go-verify/getting-started) for more
information.
## Enhanced privacy and security in GO Verify
2025-01-21
Tags: GO Verify
This release (v1.1.3) of the
[MATTR GO Verify example app](/docs/verification/go-verify/getting-started) enhances security
and privacy by blocking devices from taking screenshots or recording the verification results page.
This mitigates the risk of presenting outdated proof of verification, as well as prevents sensitive
information from being unintentionally or maliciously captured and shared.
The MATTR GO Verify example app can be branded and configured to meet all your production
verification needs. Check out our
[MATTR GO Verifier onboarding guide](/docs/verification/go-verify/getting-started) for more
information.
## Online presentation available in GO Hold
2024-12-12
Tags: GO Hold
The latest version (v3.2.0) of the **MATTR GO Hold example app** is now available, introducing
support for [online presentation](/docs/verification/remote-overview) of [mDocs](/docs/concepts/mdocs) as per
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html).
Refer to the example app [getting started](/docs/holding/go-hold/getting-started) guide to
try this new capability with a number of MATTR Labs demos.
## Fixing configuring available credential formats in GO Hold applications
2024-11-12
Tags: GO Hold
This release (v3.1.2) of the **MATTR GO Hold example app** includes a bug fix that enables
configuring available credential formats in different wallets. While the MATTR GO Hold example app
supports all available MATTR credential formats (mDocs, CWT and JSON), any branded GO Hold
applications can choose to only support some of these available formats. This fix now ensures that
any UI elements relevant to specific credential formats are not displayed when these formats are not
supported in a branded GO Hold application.
The MATTR GO Hold example app can be branded and configured to meet all your production credential
holding needs. Check out our
[MATTR GO Hold onboarding guide](/docs/holding/go-hold/getting-started) for more
information.
## Introducing the MATTR GO Hold example app
2024-10-24
Tags: GO Hold
We are releasing a new version (v3.1.1) of the app formerly known as the **MATTR Showcase wallet**,
renaming it to the **MATTR GO Hold example app**. Built using the MATTR Pi wallet SDK, this app
showcases everything the SDK has to offer.
The MATTR GO Hold example app can be branded and configured to meet all your production credential
holding needs. Check out our
[MATTR GO Hold onboarding guide](/docs/holding/go-hold/getting-started) for more
information.
## Terminology updates across MATTR platforms
2024-10-15
Tags: GO Hold, GO Verify
To make it easier to consume our capabilities, we have made some changes to our terminology to
describe credentials supported by MATTR platforms by their underlying technology and standards. The
following credential formats are supported:
* [mDocs](/docs/concepts/mdocs): Previously referred to as Mobile credentials.
* [CBOR Web Tokens (CWT) credentials](/docs/concepts/cwt): Previously referred to as Compact credentials.
* JSON credentials: Previously referred to as Web credentials.
## Introducing the MATTR GO Verify example app
2024-09-26
Tags: GO Verify
The first version (v1.0.0) of the MATTR GO Verify example app is now available in the app stores.
This app is built using the [MATTR Pi Verifier SDKs](/docs/verification/sdks/overview) and demonstrates
the full range of MATTR in-person verification capabilities:
* **[mDocs](/docs/concepts/mdocs) verification**: Request and verify mDLs and mdocs as per ISO/IEC 18013-5
and ISO/IEC TS 23220-4.
* **[CWT credentials](/docs/concepts/cwt) verification**: Instantly verify a CWT credential by scanning a QR
code displayed by the holder.
* **Real-time results**: View verification outcomes along with relevant credential information.
#### Key Functionalities [#key-functionalities]
* **Pre-configured setup**:
* Request templates to showcase verification capabilities in non-production use cases, including
support for verification of [mDocs](/docs/concepts/mdocs) and [CWT credentials](/docs/concepts/cwt).
* Fixed list of trusted issuers. Credentials issued by other entities cannot be verified by this
example app.
* **Customizable display**: Tailor the result screen to highlight either verification status or
detailed credential information.
* **Auto-hide results**: Configure the app to automatically hide verification results after a set
period for enhanced privacy.
* **Optimized scanning**: Use flashlight and reverse camera functions to improve scanning in
different environment conditions.
* **Privacy-preserving**: No data is persisted, and credential information is only visible for a
limited time required to complete the verification workflow.
The MATTR GO Verify example app can be branded and configured to meet all your production
verification needs. Check out our
[MATTR GO Verifier onboarding guide](/docs/verification/go-verify/getting-started) for more
information.
## Improved user experience in the MATTR Showcase and GO Wallets
2024-08-20
Tags: GO Hold
The latest version (v3.0.4) of our MATTR Showcase Wallet is out, with an improved user experience
across the application.
#### Key Features [#key-features]
* Revamped header and footer navigation menus offer improved usability and maximize screen view
area.
* New options for the user to interact with verification requests over in-person and remote
channels.
* Improved control during credential presentation flows when providing consent to sharing user
information.
* Enhanced security via configurable restrictions over inline and remote contexts for Web semantic
credentials.
* Upgrade to the latest MATTR Pi SDK with support for React Native 0.72.
**MATTR GO Wallet** customers will receive a new release which includes all the new features and
enhancements from our latest MATTR Showcase Wallet version.
## Clock skew tolerance in the MATTR Showcase Wallet
2024-06-25
Tags: GO Hold
The latest version (v2.9.11) of our MATTR Showcase Wallet better supports in-person Mobile
Credentials (mDL/mDoc) interactions by introducing clock skew tolerance to accommodate time
inconsistencies between the wallet and entities it is interacting with.
## Enhancements to MATTR Showcase Wallet and MATTR GO Wallet
2024-04-02
Tags: GO Hold
The latest version (v2.9.1) of our MATTR Showcase Wallet is out, expanding our supported credential
formats and offering several functional and usability enhancements.
#### Key Features [#key-features]
* Users can now claim and manage Mobile Credentials issued through the
[OID4VCI workflow](/docs/issuance/oid4vci-overview).
* Users can now respond to Mobile Credential presentation requests sent by verifiers.
* Users will now use their device authentication capabilities instead of the app authentication when
accessing the MATTR Showcase Wallet.
* Users can now easily assess the status of their credential’s validation outcome using a newly
introduced credential pill status. Clicking the pill provides further validation details.
**MATTR GO Wallet** customers will receive a new release which includes all the new features and
enhancements from our latest MATTR Showcase Wallet version.
## Enhancements to the MATTR Showcase Wallet
2024-02-08
Tags: GO Hold
The latest version (v2.8.6) of our MATTR Showcase Wallet is out!
Our latest MATTR Showcase Wallet release is mainly a technical release to improve technical
stability and consistency, but we couldn't help but include some wallet improvements along the way.
#### Key Features [#key-features]
* Migration to the latest wallet service.
* Upgrade to the latest wallet SDK.
* Miscellaneous usability enhancements and bug fixes.
## Introducing MATTR GO Verifier
2024-02-05
Tags: GO Verify
Getting started with credential verification just got easier with the launch of the new MATTR GO
Verifier. No need to develop your own credential verification app when you can customize ours with
your brand and business rules whilst leveraging MATTR’s world-class technology and expertise.
Available for both iOS and Android, the
[MATTR GO Verifier](https://files.mattr.global/specsheets/go-verify.pdf) joins
[MATTR GO Wallet](https://files.mattr.global/specsheets/go-hold.pdf) as part of the
[MATTR GO platform](https://mattr.global/platforms/go). These ready-to-go white label products are
designed to accelerate your credential verification journey by eliminating the cost, time and
complexity of developing your own apps from scratch.
[Contact us](https://mattr.global/contact-us) today to learn how easy it is to get going with
credentials verification using our world-class MATTR GO toolset.
## Enhancements to MATTR Showcase Wallet and MATTR GO Wallet
2024-01-10
Tags: GO Hold
The latest version (v2.8.5) of our MATTR Showcase Wallet is out!
To provide a better user experience and application performance when wallets include multiple
credentials and pending requests, our latest MATTR Showcase Wallet release includes several
usability enhancements and bug fixes.
#### Key Features [#key-features]
* Users can now filter their activity feed so that only messages that require attention are
displayed. For example, this can help users identify pending credential offers.
* Improved wallet performance as credential offers are now only validated when the user selects to
accept the offer.
* Improved performance as credential status is no longer displayed on the credential activity card.
**MATTR GO Wallet** customers will receive a new release which includes all the new features and
enhancements from our latest MATTR Showcase Wallet version.
## Enhancements to MATTR Showcase Wallet and MATTR GO Wallet
2023-11-16
Tags: GO Hold
The latest version (v2.8.4) of our MATTR Showcase Wallet is out!
To provide a better user experience, our latest MATTR Showcase Wallet release includes some
usability enhancements, bug fixes and copy changes to accompany the enhancements.
#### Key Features [#key-features]
* Customers can configure if auto-deletion of expired credentials and its associated activities is
turned on or off for the wallet.
* Users can set if auto-deletion of expired credentials and its associated activities is enabled or
not. Users can also set the frequency with which auto-deletion checks are done.
**MATTR GO Wallet** customers will receive a new release which includes all the new features and
enhancements from our latest MATTR Showcase Wallet version.
## Enhancements to MATTR Showcase Wallet and MATTR GO Wallet
2023-10-25
Tags: GO Hold
The latest version (v2.8.1) of our MATTR Showcase Wallet is out!
To provide a better user experience when wallets include multiple credentials, our latest MATTR
Showcase Wallet release includes several usability enhancements, bug fixes and copy changes to
accompany the enhancements.
#### Key Features [#key-features]
* Users can sort their list of credentials by collection or expiry dates.
* Users can filter their list of credentials by their status. Expired credentials are hidden by
default, but users can configure their wallet to show them when required.
* Users can immediately accept credential offers.
* Users remain on the Wallet tab when a credential is deleted.
* Share button is only available for credentials that can share a QR code.
* Activity details are auto-refreshed.
**MATTR GO Wallet** customers will receive a new release which includes all the new features and
enhancements from our latest MATTR Showcase Wallet version.
## MATTR Wallet and MATTR GO Wallet now support the BBS2022 signature suite
2023-08-15
Tags: GO Hold
The latest version (v2.7.1) of our MATTR Wallet is out!
#### Key Features [#key-features]
* To leverage its key security and efficiency enhancements, our MATTR Wallet now supports the
BBS2022 signature suite across the credential lifecycle (issue, hold, and present).
* This version also includes a migration to React Native V0.71, as well as a number of UI and
usability enhancements.
**MATTR GO Wallet** customers will receive a new release which includes all the new features and
enhancements from our latest MATTR Wallet version.
## Introducing MATTR GO: Our nimble and robust white-label apps platform
2023-08-03
Tags: GO Hold, GO Verify
We are excited to announce the official launch of our MATTR GO platform, which provides customers
with a fast and convenient way of creating a white-label branded apps for use in their credential
ecosystems.
Built on our robust MATTR Pi Wallet Toolkit, the MATTR GO Wallet leverages all the benefits of our
MATTR Wallet while allowing customers to wrap the interface in a brand their users already know and
trust.
It is interoperable and compliant with global standards, and can be integrated with existing MATTR’s
credential lifecycle capabilities, available in the MATTR VII Platform.
The MATTR GO Wallet is suitable for customers looking for a ready-to-use wallet that can be
distributed directly to credential holders in their ecosystem, with no coding required.
[Find out more](https://mattr.global/platform/go/) and refer the
[Product Specification Sheet](https://files.mattr.global/specsheets/go-hold.pdf) for further
details.
## New Features and Enhancements across MATTR platforms
2023-08-03
Tags: GO Hold, GO Verify
We are happy to announce several key improvements across our MATTR platforms, highlighted by new
self-service capabilities, enhanced APIs, and several MATTR Wallet features:
### Self-service Tenant Management [#self-service-tenant-management]
Our new suite of API endpoints support the ability to manage tenants (create, view and delete) and
analytics events in your environment. Please [contact us](mailto:dev-support@mattr.global) for more
information and/or access to this new capability.
### Preventing Issuance of Expired Credentials [#preventing-issuance-of-expired-credentials]
Our MATTR VII credential issuance API endpoints now prevent issuing any expired credentials where
the current date has passed the specified expiry date.
### MATTR Wallet and MATTR GO Release [#mattr-wallet-and-mattr-go-release]
Our recent MATTR Wallet release (V2.6.1) introduces support for claiming and storing Compact
Credentials issued through the OID4VCI protocol. In addition, it includes several UI enhancements
to further improve the wallet user experience.
**MATTR GO Wallet** customers will receive a new release which includes all new features and
enhancements from our latest MATTR Wallet version.
## MATTR Wallet - Migration to React Native V0.70
2023-03-23
Tags: GO Hold
The latest release of our MATTR Wallet is out!
Our MATTR Wallet has been migrated to React Native V0.70 as well as being upgraded to Realm V11 to
leverage key improvements with these versions. This release also includes a number of UI and
usability enhancements.
Refer to the [React Native](https://reactnative.dev/blog/2022/09/05/version-070) and
[MongoDB](https://www.mongodb.com) pages for further details on React Native V0.70 and
Realm V11.
## MATTR Wallet - Additional Language Support
2022-12-16
Tags: GO Hold
Our MATTR Wallet now supports Swiss German and French Canadian languages.
To experience the MATTR Wallet in these languages, you will need to set the correct language on your
iPhone or Android device.
Refer to the [Apple](https://support.apple.com/en-nz/HT204031) and
[Google](https://support.google.com/accounts/answer/32047?hl=en\&co=GENIE.Platform%3DAndroid) support
pages if you need to change your language settings.
## MATTR Wallet - New look and feel
2022-09-30
Tags: GO Hold
Our new MATTR Wallet is up! Experience a brand new look and feel!
### Improved journeys and flow [#improved-journeys-and-flow]
A revised navigation and structure allows you to access all the features you need in a fast and
efficient way.
### New and improved onboarding [#new-and-improved-onboarding]
New experience to guide and inform you on how to get the most out of your wallet.
### Credentials look more life-like to build trust with your users. Your credentials are our top priority! [#credentials-look-more-life-like-to-build-trust-with-your-users-your-credentials-are-our-top-priority]
Your credentials have been redesigned to look and feel like physical cards users are already
familiar with.
### Create credentials that match your organizations branding [#create-credentials-that-match-your-organizations-branding]
Credentials mimic real-world cards more than ever before. You can now customize colors, logos and
include watermarks to suit your brand needs.
### Log of all your information and sharing events plus activities [#log-of-all-your-information-and-sharing-events-plus-activities]
A log of all credential interactions you have performed with the MATTR Wallet, along with different
ways to view the events, such as grouping by connection.
# MATTR Changelogs
URL: /docs/resources/changelog
Changelog for our MATTR VII API platform.
Changelog for our MATTR Pi SDKs.
Changelog for our MATTR GO whitelabel apps.
Sign up for our newsletter to stay updated on the latest releases.
# Release newsletter signup
URL: /docs/resources/changelog/newsletter-signup
Use the form below to sign up for our release newsletter and stay updated with the latest features and improvements.
# MATTR Pi Changelog
URL: /docs/resources/changelog/pi
[Subscribe](/docs/resources/changelog/newsletter-signup) to our release
newsletter to get the latest updates on product releases, new features and bug
fixes.
## SDK Tethering and breaking changes in the Android mDocs Verifier SDK
2026-06-26
Tags: Verifier Mobile, Android
This major release (v7.0.0) of the [Android mDocs Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview#android)
introduces SDK Tethering, along with a number of breaking API changes and bug fixes.
Because this release contains breaking changes, review the
[Android 7.0.0 migration guide](/docs/verification/remote-mobile-verifiers/sdks/android-7.0.0-migration-guide)
before upgrading.
*Breaking changes*
* **SDK Tethering**: The SDK now registers each app instance with your MATTR VII tenant, giving you
operational insight into registered and active app instances and establishing a remote management
channel we expect to extend in future releases. SDK Tethering is now required: `MobileCredentialVerifier.initialize`
requires a `PlatformConfiguration` (previously optional) to configure the MATTR VII tenant used for
registration and licensing. `initialize` can now throw `InvalidLicenseException` and
`FailedToRegisterException`, and the majority of the SDK's APIs throw `InvalidLicenseException` when
a valid license is not present. The SDK uses
[Key Attestation](https://source.android.com/docs/security/features/keystore/attestation) during
registration. See the
[SDK Tethering guide](/docs/verification/remote-mobile-verifiers/sdks/sdk-tethering) for details.
* **Renamed revocation status list APIs**: The revocation status list management API has been renamed
from `TrustedIssuer`-prefixed terminology to `Revocation` terminology.
`updateTrustedIssuerStatusLists` is now `refreshRevocationStatusLists`,
`getTrustedIssuerStatusListsCacheInfo` is now `getRevocationStatusListsCacheInfo`, and
`TrustedIssuerStatusListsCacheInfo` is now `RevocationStatusListsCacheInfo`. Update all references to
the new names.
* **Result types are now sealed interfaces**: `UpdateTrustedIssuerStatusListsResult` has been renamed
to `RevocationStatusListsRefreshResult` and, along with `OnlinePresentationSessionResult`, converted
from data classes to sealed interfaces with `Success` and `Failure` variants. The `.success: Boolean`
field has been removed. Callers must replace field-based branching with `when`/`is` pattern matching.
* **`MobileCredentialResponse` arrays are now required**: `MobileCredentialResponse.credentials` and
`MobileCredentialResponse.credentialErrors` are now required non-nullable `List` fields and must be
provided at construction. Callers can remove null-check branches and `?.` navigation when accessing
these fields.
* **Application ID parameter removed**: `MobileCredentialVerifier.requestMobileCredentials` no longer
requires an `applicationId` parameter. The SDK now uses the application ID provided in
`PlatformConfiguration` during initialization. Remove the `applicationId` argument and supply it via
`PlatformConfiguration` instead.
*Bug fixes*
* Added size validation to `IssuerSigned.random` values, rejecting oversized values that could
otherwise cause excessive memory use when verifying a presented credential.
* Fixed an issue where long Status List distribution URLs would fail to parse, resulting in the SDK
not being able to download and use the list.
For a complete description of the changes, see the
[full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/).
*End of Life notice*
* In line with our SLA, major version `6.x.x` of the Android mDocs Verifier SDK has now entered its
**Maintenance** phase and will reach its **End of Life (EOL)** on **26 September 2026**, after
which it will no longer be supported.
## SDK Tethering and breaking changes in the iOS mDocs Verifier SDK
2026-06-26
Tags: Verifier Mobile, iOS
This major release (v6.0.0) of the [iOS mDocs Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview#ios)
introduces SDK Tethering, along with a number of breaking API changes, smaller enhancements, and
bug fixes.
Because this release contains breaking changes, review the
[iOS 6.0.0 migration guide](/docs/verification/remote-mobile-verifiers/sdks/ios-6.0.0-migration-guide)
before upgrading.
*New features*
* **Automatic challenge generation for remote mobile verification**: The `challenge` parameter on
`requestMobileCredentials` for remote mobile (app-to-app) verification is now optional. When
omitted or blank, the SDK generates a cryptographically secure random 32-byte challenge
automatically, removing the requirement for the caller to manage challenge generation.
* **Connectivity error reporting for remote mobile verification**: `requestMobileCredentials` for
remote mobile (app-to-app) verification now throws `MobileCredentialVerifierError.connectivityError`
if the internet connection is lost during app-to-app verification.
*Breaking changes*
* **SDK Tethering**: The SDK now registers each app instance with your MATTR VII tenant, giving you
operational insight into registered and active app instances and establishing a remote management
channel we expect to extend in future releases. SDK Tethering is now required: `platformConfiguration`
on `initialize` is mandatory and drives registration and licensing. `initialize` can now throw
`MobileCredentialVerifierError.invalidLicense` and `MobileCredentialVerifierError.failedToRegister`,
and the majority of the SDK's APIs throw `MobileCredentialVerifierError.invalidLicense` when a valid
license is not present. The SDK uses
[App Attest](https://developer.apple.com/documentation/devicecheck/establishing-your-app-s-integrity)
during registration where available. See the
[SDK Tethering guide](/docs/verification/remote-mobile-verifiers/sdks/sdk-tethering) for details.
* **Asynchronous initialization**: The `initialize` method is now asynchronous and must be awaited
from an asynchronous context.
* **Renamed and restructured types**: Several types and properties were renamed or restructured for
cross-platform consistency. `VerificationResult` is now `MobileCredentialVerificationResult` and its
`reason` property is now `failureType`, `TrustedCertificateVerificationResult.reason` is now
`failureType`, and the now-removed `VerificationFailedReason` wrapper has been replaced by directly
typed failure types. The revocation status list APIs were renamed from `TrustedIssuer`-prefixed
terminology to `Revocation` terminology: `updateTrustedIssuerStatusLists()` is now
`refreshRevocationStatusLists()` and `getTrustedIssuerStatusListsCacheInfo()` is now
`getRevocationStatusListsCacheInfo()`, with their result and cache-info types renamed to match.
* **Result types are now enums**: `RevocationStatusListsRefreshResult` and
`OnlinePresentationSessionResult` have been converted from structs with optional properties to
`@frozen` enums with `success` and `failure` cases. Callers must replace property-based branching
with `switch`/`case` pattern matching.
* **Serialization changes**: `MobileCredentialVerificationFailureType` and
`TrustedCertificateVerificationFailureType` now encode and decode as a `{type, message}` object
instead of a plain raw-value string. Update any code that persists or transports these values.
* **`applicationId` parameter removed**: The `applicationId` parameter has been removed from
`fetchAppleWalletConfiguration` and `requestMobileCredentials`. The SDK now uses the `applicationId`
from the `PlatformConfiguration` provided at initialization. Update all call sites to the new method
signatures.
* **Removed error case**: `MobileCredentialVerifierError.platformConfigurationInvalid` has been removed
and is no longer thrown by `fetchAppleWalletConfiguration`.
*Bug fixes*
* Fixed an issue where the verification result was not delivered during a proximity presentation when
the holder terminated the session immediately after sending its response (for example, with
auto-terminate enabled). The SDK now waits for response processing to complete before handling the
BLE disconnect.
* Fixed an issue where background prewarming could leave the SDK unable to initialize. The SDK now
detects and recovers from stale storage keys automatically.
For a complete description of the changes, see the
[full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/changelog).
*End of Life notice*
* In line with our SLA, major version `5.x.x` of the iOS mDocs Verifier SDK has now entered its
**Maintenance** phase and will reach its **End of Life (EOL)** on **26 September 2026**, after
which it will no longer be supported.
## Bug fixes in the React Native mDocs Holder SDK
2026-06-25
Tags: Holder, React Native
This release (v9.0.4) of the [React Native mDocs Holder SDK](/docs/holding/sdk-overview) includes the following fixes:
* On Android, fixed an issue where the SDK could crash if DCM configuration cleanup failed. Cleanup errors are now caught and logged, allowing the host app to continue safely.
* On iOS, the SDK now recovers automatically from a storage key mismatch without data loss. A stored
storage key could become stale for a number of reasons, leaving the SDK unable to initialize
against existing credential data. This was most commonly observed during background prewarming,
where the SDK could launch before the device's first unlock and be unable to read existing keychain
items. On initialization, the SDK verifies the active storage key by attempting to decrypt an
encrypted on-disk probe. If the recorded key is stale, the SDK enumerates all available keychain
candidates, identifies the one that successfully decrypts the probe, updates the stored key, and
removes the stale entry. If the probe cannot be read because of filesystem protection,
`StorageInitializedInBackground` is thrown.
For complete details, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/index.html#md:change-log).
## Status list URL parsing fix in the Android mDocs Verifier SDK
2026-06-24
Tags: Verifier Mobile, Android
Version 6.1.1 of the [Android mDocs Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview#android) is a bug fix release.
*Bug fixes*
* Fixed an issue where long status list distribution URLs would fail to parse, preventing the SDK from downloading and using the list.
For complete details and technical guidelines, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/).
## React Native SDKs EOL announcement
2026-06-23
Tags: Holder, Verifier Mobile, React Native
In line with our SLA, the following SDK versions have now reached their **End of Life (EOL)** and
are no longer supported:
* React Native mDocs Holder SDK v8.
* React Native mDocs Verifier SDK v8.
For a list of actively supported versions, please refer to the respective SDK documentation:
* [Holder SDKs](/docs/holding/sdk-overview#versions)
* [Verifier Mobile SDKs](/docs/verification/remote-mobile-verifiers/sdks/overview#versions)
If you have questions about upgrading or need support with migration, please
[get in touch](mailto:dev-support@mattr.global).
## SDK Tethering, Wallet Attestation, and breaking changes in the Android mDocs Holder SDK
2026-06-22
Tags: Holder, Android
This major release (v7.0.0) of the [Android mDocs Holder SDK](/docs/holding/sdk-overview#android)
introduces SDK Tethering and Wallet attestation, along with a number of breaking API changes, a
smaller enhancement, and bug fixes.
Because this release contains breaking changes, review the
[Android 7.0.0 migration guide](/docs/holding/sdk-operations/migration-guides/android-7.0.0-migration-guide)
before upgrading.
*New features*
* **SDK Tethering**: The SDK can now register each app instance with your MATTR VII tenant, giving
you operational insight into registered and active app instances and establishing a remote
management channel we expect to extend in future releases. Tethering is enabled by passing a new
optional `PlatformConfiguration` to `initialize`. It is currently optional but will become
required in an upcoming release. See the
[SDK Tethering guide](/docs/holding/sdk-operations/sdk-tethering) for details.
* **Wallet attestation**: You can now build holder applications that claim credentials from issuers
which restrict issuance to trusted wallet applications. When an issuer requires it, the SDK
automatically proves the application's authenticity following the OAuth 2.0
Attestation-Based Client Authentication specification. Wallet attestation requires SDK Tethering.
See the [Wallet attestation guide](/docs/holding/credential-claiming-guides/wallet-attestation)
for details.
* **Stronger application identity during issuance**: Pre-authorized issuance flows now pass the
application's `client_id`, improving compatibility with issuers applying stricter controls.
*Breaking changes*
* **Configurable biometric authentication behavior**: A new `userAuthenticationType` parameter on
`UserAuthenticationConfiguration` lets you configure biometric authentication behavior.
`initialize` may now throw if `BiometryCurrentSet` is used with `OnInitialize`.
* **Renamed and restructured types**: Several types and properties were renamed or restructured for
cross-platform consistency, including `AuthenticationOption` (now `DeviceAuthenticationOption`),
the `OfferedCredential.doctype` field (now `docType`), and the matched-credentials accessor on
`OnlinePresentationSession`, which is now the `getMatchedCredentials()` method.
`RetrieveCredentialResult` is now a sealed interface with `Success` and `Failure` variants.
* **New error cases and discovery fields**: New values were added to `RetrieveCredentialError`, new
required authorization-server fields were added to `DiscoveredCredentialOffer`, and
`OfferedCredential.claims` is now optional. Update exhaustive `when` statements and any code that
constructs these types.
* **Session termination change**: The `sessionStatus` parameter was removed from
`ProximityPresentationSession.terminateSession`; terminated sessions now send a
`SessionTerminated` status to the verifier.
*Bug fixes*
* Fixed an issue where a malformed Authorization Request JWS would throw an unexpected error.
* Added temporal validation of `AuthorizationRequest` `iat`, `exp`, and `nbf` values, which were
previously not validated (allowing replay of expired request objects).
* Fixed an NFC negotiated-handover issue on certain Samsung devices where device engagement could
get stuck in an infinite loop.
* Fixed an issue where requesting only an `age_over_xx` attribute during a DCM flow showed an empty
consent screen.
* Fixed an issue where the OID4VP `state` parameter had no size limit during online presentation.
* Fixed an issue where the DCM origin encoded the app signing certificate hash using hex instead of
unpadded base64, causing verification to fail.
* Fixed a crash during online presentation when the `x5c` header was stripped from a validly-signed
request object JWT.
* Fixed a crash caused by oversized credential fields during pre-authorized issuance.
For a complete description of the changes, see the
[full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/).
*End of Life notice*
* In line with our SLA, major version `6.x.x` of the Android mDocs Holder SDK has now entered its
**Maintenance** phase and will reach its **End of Life (EOL)** on **22 September 2026**, after
which it will no longer be supported.
## SDK Tethering, Wallet Attestation, and breaking changes in the iOS mDocs Holder SDK
2026-06-22
Tags: Holder, iOS
This major release (v6.0.0) of the [iOS mDocs Holder SDK](/docs/holding/sdk-overview#ios) introduces
SDK Tethering and Wallet attestation, along with a number of breaking API changes, smaller
enhancements, and bug fixes.
Because this release contains breaking changes, review the
[iOS 6.0.0 migration guide](/docs/holding/sdk-operations/migration-guides/ios-6.0.0-migration-guide)
before upgrading.
*New features*
* **SDK Tethering**: The SDK can now register each app instance with your MATTR VII tenant, giving
you operational insight into registered and active app instances and establishing a remote
management channel we expect to extend in future releases. Tethering is enabled by passing a new
optional `platformConfiguration` to `initialize`. It is currently optional but will become
required in an upcoming release. See the
[SDK Tethering guide](/docs/holding/sdk-operations/sdk-tethering) for details.
* **Wallet attestation**: You can now build holder applications that claim credentials from issuers
which restrict issuance to trusted wallet applications. When an issuer requires it, the SDK
automatically proves the application's authenticity following the OAuth 2.0
Attestation-Based Client Authentication specification. Wallet attestation requires SDK Tethering.
See the [Wallet attestation guide](/docs/holding/credential-claiming-guides/wallet-attestation)
for details.
* **Stronger application identity during issuance**: Pre-authorized issuance flows now pass the
application's `client_id`, improving compatibility with issuers applying stricter controls.
* **Automatic challenge generation for remote mobile verification**: The `challenge` parameter for
remote mobile (app-to-app) verification is now optional. When omitted, the SDK generates a
cryptographically secure random challenge automatically.
* **Improved biometric error handling**: Signing with a biometric-protected key that has become
invalid after re-enrollment now surfaces a clear authentication failure instead of an opaque
system error.
*Breaking changes*
* **Asynchronous initialization**: The `initialize` and `initializeAppExtension` methods are now
asynchronous and must be awaited from an asynchronous context.
* **New and improved error handling**: New error cases were added to cover license, registration,
wallet attestation, and app extension failures, and these must now be handled by your application.
* **Renamed and restructured types**: Several types and properties were renamed or restructured for
cross-platform consistency, including `MobileCredentialAuthenticationOption` (now
`DeviceAuthenticationOption`) and `VerificationResult` (now `MobileCredentialVerificationResult`).
`RetrieveCredentialResult` is now an enum with `success` and `failure` cases, and
`OnlinePresentationSession.matchedCredentials` is now the `getMatchedCredentials()` method.
* **Non-optional properties and stricter validation**: Credential and response collections such as
`MobileCredentialResponse.credentials`, `MobileCredential.claims`, and device-key authentication
types are now non-optional. Credentials with empty claims are now rejected during retrieval or
decoding. A new `.none` case on `UserAuthenticationType` lets you explicitly disable user
authentication for a device key.
* **OpenID4VCI v1.0 alignment**: The `claims` property on offered credentials is now optional, and
new authorization-server discovery properties were added to `DiscoveredCredentialOffer` to support
the finalized specification.
* **Serialization changes**: The document type now serializes under the `docType` key (previously
`doctype`), and verification failure types now serialize as a structured `{type, message}` object.
Update any code that persists or transports these values.
* **App extension log location change**: App extension logs are now stored in a new location.
No code changes are required, but logs written before upgrading will no longer be accessible.
*Bug fixes*
* Fixed an issue where verifier information was missing from activity log entries recorded during
online presentation sessions.
* Fixed an issue where a credential-added event was recorded even when every credential in an
issuance batch failed.
* Fixed an issue where credential offers without claims in issuer metadata were incorrectly rejected
during OID4VCI discovery.
* Fixed an issue where an unretrievable database encryption key caused a storage initialization
error after certain app lifecycle transitions.
* Fixed an issue where the proximity presentation session emitted a spurious request-received
callback and a redundant termination error on receiving a session status code.
* Fixed an issue where credential issuance web authentication was canceled when the app moved to the
background, preventing the authorization from completing on return.
* Fixed an issue where switching an existing instance to a different app group did not remove the
previous app group's data.
For a complete description of the changes, see the
[full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/changelog).
*End of Life notice*
* In line with our SLA, major version `5.x.x` of the iOS mDocs Holder SDK has now entered its
**Maintenance** phase and will reach its **End of Life (EOL)** on **22 September 2026**, after
which it will no longer be supported.
## New none option for device key authentication and storage key recovery in the iOS mDocs Holder SDK
2026-06-12
Tags: Holder, iOS
This release (v5.2.0) of the [iOS mDocs Holder SDK](/docs/holding/sdk-overview#ios) introduces
optional device key authentication and automatic recovery from storage key mismatches.
*New features*
* **No-authentication device keys**: Credential device keys can now be created with no user
authentication requirement, enabled by a new `none` case on `UserAuthenticationType`. Setting
`UserAuthenticationType.none` on a `DeviceKeyAuthenticationPolicy` explicitly disables
authentication for the key, while a `nil` authentication type continues to inherit the requirement
from the SDK initialization configuration as before.
*Bug fixes*
* The SDK now recovers automatically from a storage key mismatch without data loss. A stored storage
key could become stale for a number of reasons, leaving the SDK unable to initialize against
existing credential data. This was most commonly observed during background prewarming, where the
SDK could launch before the device's first unlock and be unable to read existing keychain items. On
initialization, the SDK verifies the active storage key by attempting to decrypt an encrypted
on-disk probe. If the recorded key is stale, the SDK enumerates all available keychain candidates,
identifies the one that successfully decrypts the probe, updates the stored key, and removes the
stale entry. If the probe cannot be read because of filesystem protection,
`MobileCredentialHolderError.storageInitializedInBackground` is thrown.
See the [SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/changelog) for a complete description of the changes.
## Session correlation with state in the Verifier Web SDK
2026-06-09
Tags: Verifier Web
This release (v2.2.0) of the
[Verifier Web SDK](/docs/verification/remote-web-verifiers/sdks/overview) introduces the following
enhancement:
* Added an optional `state` parameter to `requestCredentials()`. Pass an opaque correlation
reference, such as an internal record ID, to link a verification session to a record in your own
system without maintaining a separate `sessionId` mapping. MATTR VII carries the value through the
session and returns it with the result in both same-device and cross-device flows, on both
successful and failed results. Refer to
[Correlating verification sessions with your system](/docs/verification/remote-web-verifiers/guides/correlating-verification-sessions)
for more information.
Refer to the
[SDK Docs changelog](https://api-reference-sdk.mattr.global/verifier-sdk-web/latest/index.html)
for more information.
## Bug fix in the React Native mDocs Verifier SDK
2026-06-04
Tags: Verifier Mobile, React Native
This release (v9.0.3) of the [React Native mDocs Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview) includes the following fix:
* Fixed an issue on iOS where verification results were not displayed during proximity presentation
sessions when BLE mode was set to Peripheral Server and Auto Terminate Session was enabled.
For complete details, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/index.html#md:change-log).
## Bug fix in the iOS mDocs Verifier SDK
2026-06-03
Tags: Verifier Mobile, iOS
This release (v5.1.1) of the [iOS mDocs Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview#ios) includes the following fix:
* Fixed an issue where verification results were not displayed during proximity presentation sessions
when BLE mode was set to Peripheral Server and Auto Terminate Session was enabled.
For complete details, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/changelog).
## Bug fix in the Android mDocs Holder SDK
2026-05-25
Tags: Holder, Android
This release (v6.1.4) of the [Android mDocs Holder SDK](/docs/holding/sdk-overview#android) includes the following fix:
* Fixed an issue where the SDK could crash if DCM configuration cleanup failed. Cleanup errors are
now caught and logged, allowing the host app to continue safely.
For complete details, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/).
## Bug fixes in the React Native mDocs Holder SDK
2026-05-18
Tags: Holder, React Native
This release (v8.1.3) of the [React Native mDocs Holder SDK](/docs/holding/sdk-overview) includes the following iOS platform fixes:
* Improved early app launch handling during SDK initialization. The SDK now probes Keychain
accessibility before initialization proceeds, ensuring the `storageKeyID` can be loaded reliably
before storage is accessed. This enhances protection against prewarming scenarios where Keychain
items may be temporarily unavailable, reducing the likelihood of inconsistent storage state or
unrecoverable initialization failures.
* Resolved an issue that caused a crash when decoding `DiscoveredCredentialOffer` on devices running
iOS 17.0 or earlier.
* The SDK now supports ES256, ES384, and ES512 signature algorithms, expanding compatibility with a
broader range of ECDSA-based cryptographic operations.
For complete details, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/index.html#md:change-log).
## Bug fix in the iOS mDocs Holder SDK
2026-05-14
Tags: Holder, iOS
This release (v4.4.1) of the [iOS mDocs Holder SDK](/docs/holding/sdk-overview) includes the following fix:
* Improved early iOS app launch handling during SDK initialization. The SDK now probes Keychain
accessibility before initialization proceeds, ensuring the `storageKeyID` can be loaded reliably
before storage is accessed. This enhances protection against prewarming scenarios where Keychain
items may be temporarily unavailable, reducing the likelihood of inconsistent storage state or
unrecoverable initialization failures.
## Bug fix in the React Native mDocs Verifier SDK
2026-05-13
Tags: Verifier Mobile, React Native
This release (v9.0.2) of the [React Native mDocs Verifier SDK](/docs/verification/sdks/overview) includes the following fix:
* Fixed an issue on Android devices where status checks could not be turned off. The SDK now correctly
honors the `checkStatus` option, allowing apps to disable status checks on Android devices when required.
For complete details, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/index.html#md:change-log).
## SDK EOL announcement
2026-05-13
Tags: Holder, Verifier Mobile, iOS, Android
In line with our SLA, the following SDK versions have now reached their **End of Life (EOL)** and
are no longer supported:
* iOS mDocs Holder SDK v4.
* iOS mDocs Verifier SDK v4.
* Android mDocs Holder SDK v5.
* Android mDocs Verifier SDK v5.
For a list of actively supported versions, please refer to the respective SDK documentation:
* [Holder SDKs](/docs/holding/sdk-overview#versions)
* [Verifier Mobile SDKs](/docs/verification/remote-mobile-verifiers/sdks/overview#versions)
If you have questions about upgrading or need support with migration, please
[get in touch](mailto:dev-support@mattr.global).
## Bug fix in the React Native mDocs Holder SDK
2026-05-12
Tags: Holder, React Native
This release (v9.0.3) of the [React Native mDocs Holder SDK](/docs/holding/sdk-overview) includes the following fix:
* Added `None` as a `DeviceKeyAuthenticationType` option. This allows credentials to be generated and/or retrieved with no
user authentication requirement, even when the SDK is initialized with `userAuthenticationBehavior` set to `Always` or
`OnDeviceKeyAccess`.
You can now pass `{ authenticationPolicy: { type: "None" } }` to `retrieveCredentials`,
`retrieveCredentialsUsingAuthorizationSession`, or `generateDeviceKey` to opt out of authentication for a specific
device key.
For complete details, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/index.html#md:change-log).
## Bug fixes in the React Native mDocs Holder SDK
2026-04-28
Tags: Holder, React Native
This release (v9.0.2) of the [React Native mDocs Holder SDK](/docs/holding/sdk-overview) includes the following fixes:
* General bug fixes:
* Fixed an issue where the SDK did not use the OID4VCI nonce endpoint during credential issuance when required by the issuer. The SDK now automatically fetches an issuer-provided nonce from `nonceEndpoint` (when it is advertised in the issuer metadata) and includes it in the device key proof-of-possession JWT, improving compliance with the OID4VCI specification.
* Fixed an issue where calling `discoverCredentialOffer` would fail when credential metadata did not include any claims. The `claims` field is now treated as optional and defaults to an empty array when absent.
* iOS specific bug fixes:
* Fixed an issue where SDK initialization could fail on iOS devices due to inconsistent storage states, such as orphaned keychain keys after an app reinstall or transferred files without keys after device migration. The SDK now performs a preflight check and automatically recovers from these states.
* Fixed an issue where SDK storage files and directories could be included in iOS device backups, resulting in encrypted data being backed up without the keys required to restore it. These files are now excluded from iCloud and local backups.
* Fixed an issue where calling the `destroy` function did not fully remove persisted data on iOS devices. It now also deletes the database encryption key and storage key ID from the keychain, preventing orphaned keychain entries.
For complete details, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/index.html#md:change-log).
## Bug fixes in the Android mDocs Holder SDK
2026-04-10
Tags: Holder, Android
This release (v6.1.2) of the [Android mDocs Holder SDK](/docs/holding/sdk-overview#android) includes the following fixes:
* Fixed an issue where the SDK did not use the OID4VCI nonce endpoint during credential issuance when required by the issuer. The SDK now automatically fetches an issuer-provided nonce from `nonceEndpoint` (when it is advertised in the issuer metadata) and includes it in the device key proof-of-possession JWT, improving compliance with the OID4VCI specification.
* Fixed an issue where calling `discoverCredentialOffer` would fail when credential metadata did not include the mDoc IACA's URI. The `mdocIacasUri` field is now treated as optional and defaults to an empty string when absent.
* Fixed an issue where credentials containing unsupported claims could be stored but could not be retrieved. The SDK now rejects these credentials instead of storing them.
* Fixed an issue where CBOR date-time values with nanosecond accuracy could not be parsed.
For complete details, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/).
## Bug fixes in the iOS mDocs Holder SDK
2026-04-10
Tags: Holder, iOS
This release (v5.1.1) of the [iOS mDocs Holder SDK](/docs/holding/sdk-overview#ios) includes the following fixes:
* Fixed an issue where the SDK did not use the OID4VCI nonce endpoint during credential issuance when required by the issuer. The SDK now automatically fetches an issuer-provided nonce from `nonceEndpoint` (when it is advertised in the issuer metadata) and includes it in the device key proof-of-possession JWT, improving compliance with the OID4VCI specification.
* Fixed an issue where calling `discoverCredentialOffer` would fail when credential metadata did not include any claims. The `claims` field is now treated as optional and defaults to an empty array when absent.
* Fixed an issue where SDK initialization could fail due to inconsistent storage states, such as orphaned keychain keys after an app reinstall or transferred files without keys after device migration. The SDK now performs a preflight check and automatically recovers from these states.
* Fixed an issue where SDK storage files and directories could be included in device backups, resulting in encrypted data being backed up without the keys required to restore it. These files are now excluded from iCloud and local backups.
* Fixed an issue where calling the `destroy` function did not fully remove persisted data. It now also deletes the database encryption key and storage key ID from the keychain, preventing orphaned keychain entries.
For complete details, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/changelog).
## Bug fix in the React Native mDocs Holder SDK
2026-03-25
Tags: Holder, React Native
This release (v9.0.1) of the [React Native mDocs Holder SDK](/docs/holding/sdk-overview) fixes an issue where native binaries were not included in the builds published to NPM.
Version 9.0.0 of the React Native mDocs Holder SDK was non-functional and has
been removed from NPM. Use v9.0.1 or later.
## Bug fixes in the React Native mDocs Verifier SDK
2026-03-25
Tags: Verifier Mobile, React Native
This release (v9.0.1) of the [React Native mDocs Verifier SDK](/docs/verification/sdks/overview) includes the following fixes:
* Fixed an issue where native binaries were not included in the builds published to NPM.
* Fixed an iOS/Android inconsistency in `requestCredentials` to normalize behavior across platforms.
Version 9.0.0 of the React Native mDocs Verifier SDK was non-functional and
has been removed from NPM. Use v9.0.1 or later.
## App-to-app verification and Apple Wallet support in the React Native mDocs Verifier SDK
2026-03-23
Tags: Verifier Mobile, React Native
We have released a new major version (v9.0.0) of the [React Native mDocs Verifier SDK](/docs/verification/sdks/overview).
Version 9.0.0 was non-functional and has been removed from NPM. Use v9.0.1 or
later.
*Breaking changes*
* **Updated initialize signature**: The `initialize` method now accepts an optional options object and returns a `Result` type. All call sites must handle the result and possible errors.
* **Simplified status check parameter**: Replaced `skipStatusCheck` with `checkStatus` to improve readability. When set to `true` (default), revocation status is checked as part of credential verification. When set to `false`, revocation status is not checked.
* **New proximity session termination error**: Added `Exception` value to `ProximityPresentationSessionTerminationErrorType` as a fallback for unexpected issues during presentation. Exhaustive switches must handle the new case.
* **NFC error handling update**: The `registerForNfcDeviceEngagement` method now routes parse failures to `onError` instead of rethrowing.
*New features*
* **App-to-app verification**: The SDK now supports requesting credentials from another app on the same device using OID4VP, allowing verification flows to be built directly into your apps.
* **Verify with Apple Wallet (iOS only)**: The SDK can now request and verify a credential directly from Apple Wallet using the Verify with Wallet API. Only available for iOS 16 and above.
* **Logger configuration**: The `initialize` method now supports an optional `loggerConfiguration` parameter to configure the logging level.
* **New destroy method**: A new `destroy` method completely resets the SDK state, clearing all persisted storage and permanently deleting all stored certificates.
* **Status Lists Draft 14 support**: The SDK now supports the [Token Status List Draft 14](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-status-list-14) specification while maintaining existing support for Draft 3.
* **Updated COSE algorithm support**: Updated COSE algorithms as per [RFC 9864](https://www.rfc-editor.org/rfc/rfc9864.html) strengthen cryptographic compatibility and ensure continued compliance with evolving standards.
This release also includes improved BLE reliability and general stability improvements. For complete details, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/index.html#md:change-log).
*End of Life notice*
In line with our SLA, major version `8.x.x` of the React Native mDocs Verifier SDK has now entered its **Maintenance** phase and will reach its **End of Life (EOL)** on **23 June 2026**, after which it will no longer be supported.
## DC API support, device key authentication and NFC engagement in the React Native mDocs Holder SDK
2026-03-23
Tags: Holder, React Native
We have released a new major version (v9.0.0) of the [React Native mDocs Holder SDK](/docs/holding/sdk-overview).
Version 9.0.0 was non-functional and has been removed from NPM. Use v9.0.1 or
later.
*Breaking changes*
* **OID4VCI v1.0 alignment**: The SDK now implements the finalized OpenID for Verifiable Credential Issuance (OID4VCI) v1.0 specification, upgrading from draft-12. This introduces a new mandatory `credentialConfigurationId` property on `OfferedCredential`. Compatibility with issuers implementing earlier OID4VCI draft versions is no longer guaranteed.
* **Simplified status check parameter**: Replaced `skipStatusCheck` with `fetchUpdatedStatusList` to improve readability. When set to `true` (default), the SDK fetches the latest revocation status list from the server. When set to `false`, it uses cached revocation status if valid.
* **New initialize error types**: The `initialize` method can now return three new error types: `StorageInitializedInBackground`, `SdkInitialized`, and `InvalidInstanceID`, providing more specific feedback on initialization failures.
* **New proximity session termination error**: Added `Exception` value to `ProximityPresentationSessionTerminationErrorType` as a fallback for unexpected issues during presentation. Exhaustive switches must handle the new case.
* **Web callback activity path update (Android only)**: The `WebCallbackActivity` class path has moved from `global.mattr.mobilecredential.common.webcallback.WebCallbackActivity` to `global.mattr.mobilecredential.holder.webcallback.WebCallbackActivity`. This only affects Holder apps using web callbacks and requires an Android Manifest update (not a general package import change).
*New features*
* **Digital Credentials API (DC API) support**: Added support for the DC API, as defined in ISO/IEC 18013-7 Annex C (iOS) and Annex D (Android). This allows wallet apps to register stored credentials with the operating system, enabling them to appear in the DC API's selector UI when a verifier requests credentials. A new optional `dcConfiguration` parameter on `initialize` controls DC API behavior per platform.
* **Verifier authentication**: Added support for mDoc Reader Authentication as defined in ISO/IEC 18013-5:2021. The SDK can now inspect the verifier authentication result and enable users to decide whether to share credentials with an unauthenticated verifier.
* **Device key authentication**: You can now specify a per-credential authentication policy that defines what user authentication (such as biometrics or device passcode) is required to claim and access a credential.
* **NFC proximity presentation (Android only)**: The SDK now supports NFC device engagement on Android, enabling proximity presentation sessions to be started by tapping an NFC-enabled verifier terminal.
* **Status Lists Draft 14 support**: The SDK now supports the [Token Status List Draft 14](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-status-list-14) specification while maintaining existing support for Draft 3.
* **Updated COSE algorithm support**: Updated COSE algorithms as per [RFC 9864](https://www.rfc-editor.org/rfc/rfc9864.html) strengthen cryptographic compatibility and ensure continued compliance with evolving standards.
This release also includes general stability and performance improvements. For complete details, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/index.html#md:change-log).
*End of Life notice*
In line with our SLA, major version `8.x.x` of the React Native mDocs Holder SDK has now entered its **Maintenance** phase and will reach its **End of Life (EOL)** on **23 June 2026**, after which it will no longer be supported.
## Activity log bug fix in the Android mDocs Holder SDK
2026-03-12
Tags: Holder, Android
This release (v6.1.1) of the Android mDocs Holder SDK fixes an issue where presentation activity events were logged in some cases when a presentation session was not completed successfully (for example, if authentication was canceled).
## Activity logging and status list updates in the Android mDocs Holder SDK
2026-03-06
Tags: Holder, Android
Version 6.1.0 of the [Android mDocs Holder SDK](/docs/holding/sdk-overview#android) introduces activity logging and expanded status list support.
*New features*
* **Activity Log**: Wallet applications can now record and query credential and presentation events, providing a verifiable history of when credentials were added, removed, or presented. Activity logging is configured via `ActivityLogConfiguration` on the `initialize` method and can be enabled or disabled at runtime.
* **Token Status List Draft 14 support**: The SDK now supports [Token Status List Draft 14](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-status-list-14) alongside the existing Draft 3 support, including updated content types, CWT claim keys, and status list URL formats. The SDK also respects rate limit response headers returned with HTTP 429 responses from status list endpoints, with a configurable default delay for rate-limited requests.
*Bug fixes*
* Fixed an issue that prevented retrieving credentials without a `Branding.name` value.
* Fixed an issue that prevented credential presentation using DCM when `DeviceKeyPolicy` was configured with a timeout of 0.
* Fixed an issue where session engagement could hang on Pixel 7 devices.
For complete details and technical guidelines, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/).
## Activity logging and status list updates in the iOS mDocs Holder SDK
2026-03-06
Tags: Holder, iOS
Version 5.1.0 of the [iOS mDocs Holder SDK](/docs/holding/sdk-overview#ios) introduces activity logging and expanded status list support.
*New features*
* **Activity Log**: Wallet applications can now record and query credential and presentation events, providing a verifiable history of when credentials were added, removed, or presented. Activity logging is configured via `ActivityLogConfiguration` on the `initialize` method and can be enabled or disabled at runtime.
* **Token Status List Draft 14 support**: The SDK now supports [Token Status List Draft 14](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-status-list-14) alongside the existing Draft 3 support, including updated content types, CWT claim keys, and status list URL formats. The SDK also respects rate limit response headers returned with HTTP 429 responses from status list endpoints, with a configurable default delay for rate-limited requests.
For complete details and technical guidelines, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/changelog).
## Status list updates and teardown support in the Android mDocs Verifier SDK
2026-03-06
Tags: Verifier Mobile, Android
Version 6.1.0 of the [Android mDocs Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview#android) adds expanded status list support and a new SDK teardown function.
*New features*
* **Token Status List Draft 14 support**: The SDK now supports [Token Status List Draft 14](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-status-list-14) alongside the existing Draft 3 support, including updated content types, CWT claim keys, and status list URL formats. The SDK also respects rate limit response headers returned with HTTP 429 responses from status list endpoints, with a configurable default delay for rate-limited requests.
* **SDK teardown**: A new `MobileCredentialVerifier.destroy()` function allows complete teardown of the SDK instance, clearing all stored data and resetting internal state. This is useful for scenarios such as user logout or account switching.
*Bug fixes*
* Fixed an issue where session engagement could hang on Pixel 7 devices.
For complete details and technical guidelines, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/).
## Status list updates in the iOS mDocs Verifier SDK
2026-03-06
Tags: Verifier Mobile, iOS
Version 5.1.0 of the [iOS mDocs Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview#ios) adds expanded status list support.
*New features*
* **Token Status List Draft 14 support**: The SDK now supports [Token Status List Draft 14](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-status-list-14) alongside the existing Draft 3 support, including updated content types, CWT claim keys, and status list URL formats. The SDK also respects rate limit response headers returned with HTTP 429 responses from status list endpoints, with a configurable default delay for rate-limited requests.
*Bug fixes*
* Fixed an issue where HTTP 429 (Too Many Requests) responses did not respect the `Retry-After` header, potentially causing repeated rate-limited requests.
For complete details and technical guidelines, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/changelog).
## Bug fix in the React Native mDocs Holder SDK
2026-02-25
Tags: Holder, React Native
This release (8.1.2) of the [React Native mDocs Holder SDK](/docs/holding/sdk-overview) fixed an issue on Android devices that prevented retrieving credentials when a `Branding.name` value was not set.
## React Native mDocs Verifier SDK maintenance release
2026-02-25
Tags: Verifier Mobile, React Native
This release (v8.1.1) of the [React Native mDocs Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview) is a maintenance release. No new features or breaking changes were introduced.
## Android mDocs Verifier SDK maintenance release
2026-02-23
Tags: Verifier Mobile, Android
This release (v5.3.2) of the [Android mDocs Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview) is a maintenance release. No new features or breaking changes were introduced.
## Bug fix in the Android mDocs Holder SDK
2026-02-23
Tags: Holder, Android
This release (5.3.2) of the [Android mDocs Holder SDK](/docs/holding/sdk-overview) fixed an issue that prevented retrieving credentials when a `Branding.name` value was not set.
## OID4VCI v1.0 support and Digital Credential Manager integration in the Android mDocs Holder SDK
2026-02-13
Tags: Holder, Android
We have released a new major version (v6.0.0) of the [Android mDocs Holder SDK](/docs/holding/sdk-overview#android).
*Breaking changes*
* **OID4VCI v1.0 alignment**: The SDK now implements the finalized OpenID for Verifiable Credential Issuance (OID4VCI) v1.0 specification, upgrading from draft-12. This introduces a new mandatory `credentialConfigurationId` parameter to `OfferedCredential`. Compatibility with issuers implementing earlier OID4VCI draft versions is no longer guaranteed.
* **Simplified status check parameter**: Replaced `skipStatusCheck` with `fetchUpdatedStatusList` to improve readability. When set to `true` (default), the SDK fetches the latest revocation status list from the server. When set to `false`, it uses cached revocation status if valid.
* **Improved NFC Device Engagement interface**: Updated the NFC Device Engagement API to improve behavior in cases when multiple NFC-capable mDocs holders are installed on the device.
* **Verification result always returned**: The `MobileCredential.verificationResult` parameter is no longer optional and is always returned.
* **New verifier authentication result**: Introduced `VerifierAuthenticationResult.Unsigned` subtype for cases where the `DeviceRequest` does not include `readerAuth`. The `verifierAuthenticationResult` parameter is now required in `MobileCredentialRequest`.
* **Package path update**: The `mattr.global.mobilecredentials.common` package has moved to `mattr.global.mobilecredentials.holder`.
*New features*
* **Digital Credential Manager (DCM) support**: Added support for Android's Digital Credential Manager, as defined in ISO/IEC 18013-7 Annex D. This allows wallet apps to register stored credentials with the system, enabling them to appear in DCM's selector UI when a verifier requests credentials.
* **Improved NFC handling**: Added `MobileCredentialHolder.Nfc.onActivityResume` and `MobileCredentialHolder.Nfc.onActivityPause` methods to better suppress the system NFC disambiguation prompt when your application is ready to handle requests.
* **Updated COSE Algorithm Support**: The SDK now aligns with the latest COSE algorithm registrations defined in [RFC 9864](https://www.rfc-editor.org/rfc/rfc9864.html), ensuring compatibility with modern cryptographic standards.
This release also includes bug fixes for status list parsing, BLE connection issues on Pixel devices, and NFC engagement reliability. For complete details, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/).
*End of Life notice*
In line with our SLA, major version `5.x.x` of the Android mDocs Holder SDK has now entered its **Maintenance** phase and will reach its **End of Life (EOL)** on **13 May 2026**, after which it will no longer be supported.
## OID4VCI v1.0 support and Digital Credentials API integration in the iOS mDocs Holder SDK
2026-02-13
Tags: Holder, iOS
We have released a new major version (v5.0.0) of the [iOS mDocs Holder SDK](/docs/holding/sdk-overview#ios).
*Breaking changes*
* **OID4VCI v1.0 alignment**: The SDK now implements the finalized OpenID for Verifiable Credential Issuance (OID4VCI) v1.0 specification, upgrading from draft-12. This introduces a new mandatory `credentialConfigurationId` parameter to `OfferedCredential`. Compatibility with issuers implementing earlier OID4VCI draft versions is no longer guaranteed.
* **Simplified status check parameter**: Replaced `skipStatusCheck` with `fetchUpdatedStatusList` to improve readability. When set to `true` (default), the SDK fetches the latest revocation status list from the server. When set to `false`, it uses cached revocation status if valid.
* **Verification result always returned**: The `MobileCredential.verificationResult` parameter is no longer optional and is always returned.
* **Enhanced verifier authentication**: Introduced a new `unsigned` case to `VerifierAuthenticationResult` for scenarios where the `DeviceRequest` does not include `readerAuth`. The `verifierAuthenticationResult` parameter in `MobileCredentialRequest` is now required.
* **Synchronous deinitialization**: The `deinitialize` function is now synchronous, resolving concurrency-related crashes.
* **New error type**: Added `storageInitializedInBackground` error type to `MobileCredentialHolderError`, thrown when the SDK is initialized in the background. This prevents initialization issues during app prewarming.
*New features*
* **Digital Credentials API (DC API) support**: Added support for the DC API, as defined in ISO/IEC 18013-7 Annex C. This allows wallet apps to register stored credentials with the operating system, enabling them to appear in DC API's selector UI when a verifier requests credentials.
* **Updated COSE Algorithm Support**: The SDK now aligns with the latest COSE algorithm registrations defined in [RFC 9864](https://www.rfc-editor.org/rfc/rfc9864.html), ensuring compatibility with modern cryptographic standards.
This release also includes bug fixes for status list parsing with non-standard bit sizes and credential metadata serialization. For complete details, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/changelog).
*End of Life notice*
In line with our SLA, major version `4.x.x` of the iOS mDocs Holder SDK has now entered its **Maintenance** phase and will reach its **End of Life (EOL)** on **13 May 2026**, after which it will no longer be supported.
## Updated COSE algorithm support and simplified integration in the Android mDocs Verifier SDK
2026-02-13
Tags: Verifier Mobile, Android
We have released a new major version (v6.0.0) of the [Android mDocs Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview#android).
*Breaking changes*
* **Simplified status check parameter**: Replaced `skipStatusCheck` with `checkStatus` to improve readability and reduce integration confusion. When set to `true` (default), revocation status is checked as part of credential verification. When set to `false`, revocation status is not checked.
* **Package path update**: The `mattr.global.mobilecredentials.common` package has moved to `mattr.global.mobilecredentials.verifier`.
*New features*
* **Bundled Common dependency**: The `Common` dependency is now bundled into the Verifier SDK and no longer needs to be included separately, simplifying integration.
* **Updated COSE Algorithm Support**: The SDK now aligns with the latest COSE algorithm registrations defined in [RFC 9864](https://www.rfc-editor.org/rfc/rfc9864.html), ensuring compatibility with modern cryptographic standards.
This release also includes bug fixes for status list parsing, BLE retry handling, and DCM cross-device flow improvements. For complete details, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/).
*End of Life notice*
In line with our SLA, major version `5.x.x` of the Android mDocs Verifier SDK has now entered its **Maintenance** phase and will reach its **End of Life (EOL)** on **13 May 2026**, after which it will no longer be supported.
## Updated COSE algorithm support in the iOS mDocs Verifier SDK
2026-02-13
Tags: Verifier Mobile, iOS
We have released a new major version (v5.0.0) of the [iOS mDocs Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview#ios).
*Breaking changes*
* **New error type**: Added `storageInitializedInBackground` error type to `MobileCredentialVerifierError`, thrown when the SDK is initialized in the background. This prevents initialization issues during app prewarming.
* **Simplified status check parameter**: Replaced `skipStatusCheck` with `checkStatus` to improve readability. When set to `true` (default), revocation status is checked as part of credential verification. When set to `false`, revocation status is not checked.
*New features*
* **Updated COSE Algorithm Support**: The SDK now aligns with the latest COSE algorithm registrations defined in [RFC 9864](https://www.rfc-editor.org/rfc/rfc9864.html), ensuring compatibility with modern cryptographic standards.
This release also includes bug fixes for status list parsing with non-standard bit sizes and BLE presentation session timeout handling. For complete details, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/changelog).
*End of Life notice*
In line with our SLA, major version `4.x.x` of the iOS mDocs Verifier SDK has now entered its **Maintenance** phase and will reach its **End of Life (EOL)** on **13 May 2026**, after which it will no longer be supported.
## Security update in the Verifier Web SDK
2026-02-04
Tags: Verifier Web
This release (v2.1.1) of the Verifier Web SDK updated internal dependencies to address known vulnerabilities and improve overall security stability.
## React Native SDKs EOL announcement
2025-12-12
Tags: Holder, Verifier Mobile, React Native
In line with our SLA, the following SDK versions have now reached their **End of Life (EOL)** and
are no longer supported:
* React Native mDocs Holder SDK v7.
* React Native Holder SDK v11.
* React Native mDocs Verifier SDK v7.
* React Native Verifier SDK v7.
For a list of actively supported versions, please refer to the respective SDK documentation:
* [Holder SDKs](/docs/holding/sdk-overview#versions)
* [Verifier Mobile SDKs](/docs/verification/remote-mobile-verifiers/sdks/overview#versions)
If you have questions about upgrading or need support with migration, please
[get in touch](mailto:dev-support@mattr.global).
## Enhanced security and cryptographic support in the iOS Holder SDK
2025-12-02
Tags: Holder, iOS
This release (v4.4.0) of the [iOS mDocs Holder SDK](/docs/holding/sdk-overview) introduces improved security features and expanded cryptographic algorithm support.
*New features*
* **HTTPS redirect URI support**: The SDK now supports HTTPS redirect URIs registered through Associated Domains (`webcredentials:`), in addition to existing custom URL scheme redirects. This enables more secure, standards-based authentication flows and simplifies integration for apps that share a verified web domain. This feature is only available on iOS 17.4+.
* **Extended ESP algorithm support**: The SDK now also supports the `ESP256`, `ESP384`, and `ESP512` signature algorithms, expanding compatibility with a broader range of ESP-based cryptographic operations.
*Bug fixes*
* Resolved an issue where early app launch (e.g., during prewarming) could cause `UserDefaults` to be inaccessible, resulting in the `storageKeyID` failing to load and incorrect initialization. The SDK now waits for protected data availability before accessing and persists the `storageKeyID` in the Keychain, ensuring secure and reliable access once protected data becomes available.
See the [SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/changelog) for a complete description of the changes.
## Android mDocs Verifier SDK maintenance release
2025-11-27
Tags: Verifier Mobile, Android
This release (v5.3.1) of the [Android mDocs Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview) is a maintenance release. It includes internal improvements to support future capabilities.
## Bug fix in the Android Holder SDK
2025-11-27
Tags: Holder, Android
This release (5.3.1) of the [Android mDocs Holder SDK](/docs/holding/sdk-overview) fixed an issue where cached revocation status was ignored.
See the [SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/) for a complete description of the bug and fix.
## Android mDocs Verifier SDK maintenance release
2025-11-21
Tags: Verifier Mobile, Android
This release (v5.3.0) of the [Android mDocs Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview) is a maintenance release. It includes internal improvements to support future capabilities.
## NFC Device Engagement support in the Android mDocs Holder SDK
2025-11-21
Tags: Holder, Android
This release (v5.3.0) of the [Android mDocs Holder SDK](/docs/holding/sdk-overview) introduces support for NFC Device Engagement as specified in [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
Applications using the SDK can now initiate proximity presentation sessions over NFC.
See the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/) for a complete description of NFC Device Engagement support and all other changes in this release.
## Bug fix in the iOS mDocs Holder SDK
2025-11-20
Tags: Holder, iOS
This release (v4.3.1) of the [iOS mDocs Holder SDK](/docs/holding/sdk-overview) resolved an issue that caused a crash when decoding [`DiscoveredCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/discoveredcredentialoffer) on devices running iOS 17.0 or earlier.
## React Native Holder SDKs EOL announcement
2025-11-19
Tags: Holder, React Native
In line with our SLA, the following SDK versions have now reached their **End of Life (EOL)** and
are no longer supported:
* React Native Holder SDK v10.
* React Native mDocs Holder SDK v6.
For a list of actively supported versions, please refer to the [Holder SDKs](/docs/holding/sdk-overview#versions) overview page.
If you have questions about upgrading or need support with migration, please [get in touch](mailto:dev-support@mattr.global).
## Bug fix in the React Native mDocs Holder SDK
2025-11-18
Tags: Holder, React Native
This release (v8.1.1) of the [React Native mDocs Holder SDK](/docs/holding/sdk-overview) resolved an issue where calls to [`retrieveCredentialsUsingAuthorizationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/retrieveCredentialsUsingAuthorizationSession.html) would fail on iOS devices running iOS 17.0 or earlier.
## Bug fix in the iOS mDocs Holder SDK
2025-11-13
Tags: Holder, iOS
This release (v4.2.1) of the [iOS mDocs Holder SDK](/docs/holding/sdk-overview) resolved an issue that caused a crash when decoding [`DiscoveredCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/discoveredcredentialoffer) on devices running iOS 17.0 or earlier.
## Device key authentication support in the iOS mDocs Holder SDK
2025-11-06
Tags: Holder, iOS
We have released a new version (v4.3.0) of the [iOS mDocs Holder SDK](/docs/holding/sdk-overview).
*New features*
**Device key authentication**: Fine-grained control over credential security is now available through the `DeviceKeyAuthenticationPolicy` parameter. This feature allows you to specify what user authentication methods (such as Face ID, Touch ID, or device passcode) are required when claiming and accessing credentials. By configuring a per-credential authentication policy, you can require stronger methods for sensitive credentials while allowing more convenient access for less sensitive ones. The authentication requirement is bound to the device key protecting the credential, ensuring consistent security throughout the credential's lifecycle. See [Device Key Authentication](/docs/holding/credential-claiming-guides/device-key-authentication-guide) for more information.
For complete details, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/changelog).
## Android mDocs Verifier SDK maintenance release
2025-11-05
Tags: Verifier Mobile, Android
This release (v5.2.0) of the [Android mDocs Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview) is a maintenance release. It includes internal improvements to support future capabilities.
## Device key authentication support in the Android mDocs Holder SDK
2025-11-05
Tags: Holder, Android
We have released a new version (v5.2.0) of the [Android mDocs Holder SDK](/docs/holding/sdk-overview).
*New features*
**Device key authentication**: Fine-grained control over credential security is now available through the `DeviceKeyAuthenticationPolicy` parameter. This feature allows you to specify what user authentication methods (such as fingerprint authentication, face authentication, or device passcode) are required when claiming and accessing credentials. By configuring a per-credential authentication policy, you can require stronger methods for sensitive credentials while allowing more convenient access for less sensitive ones. The authentication requirement is bound to the device key protecting the credential, ensuring consistent security throughout the credential's lifecycle. See [Device Key Authentication](/docs/holding/credential-claiming-guides/device-key-authentication-guide) for more information.
For complete details, refer to the [full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/).
## mDocs Verifier SDKs maintenance releases
2025-10-23
Tags: Verifier Mobile
This release (v8.1.0) of the [React Native mDocs Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview) is a maintenance release. It includes code refactoring and improvements to support future capabilities.
## OID4VCI manual redirect support and bug fixes in the React Native mDocs Holder SDK
2025-10-23
Tags: Holder, React Native
We have released a new version (v8.1.0) of the [React Native mDocs Holder SDK](/docs/holding/sdk-overview).
*Enhancements*
**OID4VCI manual redirect support**: Allows applications to implement the OID4VCI issuance workflow within embedded WebViews while maintaining full control over the redirect flow. Applications can now:
1. Call the new `createAuthorizationSession` method to initiate the flow. This returns an `AuthorizationSession` object
containing both the `authorizeUrl` and `codeVerifier` properties.
2. Load the returned `authorizeUrl` in a WebView to handle user authentication and consent.
3. After successful authentication, capture the redirect using the configured `redirectUri`, then extract the
authorization code from the returned URL.
4. Complete the issuance workflow by calling the new `retrieveCredentialsUsingAuthorizationSession` method, passing in
the `AuthorizationSession` and extracted authorization code.
*Bug fixes*
* iOS:
* Fixed an issue where the passcode fallback button was not displayed when biometric authentication failed and
`UserAuthenticationType` was set to `.biometricOrPasscode`.
* Fixed an issue where calling `addCredential` with a credential that could not be verified against any stored trusted
issuer certificate incorrectly threw `AddMobileCredentialError.certificateNotFound` instead of
`AddMobileCredentialError.invalidCredential`.
* Android:
* Fixed an issue where closing the embedded browser during the OID4VCI authorization flow caused
`retrieveCredentials` to hang indefinitely.
## Android mDocs Verifier SDK maintenance release
2025-10-10
Tags: Verifier Mobile, Android
This release (v5.1.0) of the [Android mDocs Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview) is a maintenance release. It includes code refactoring and improvements to support future capabilities.
## OID4VCI manual redirect support and bug fixes in the Android mDocs Holder SDK
2025-10-10
Tags: Holder, Android
We have released a new version (v5.1.0) of the [Android mDocs Holder SDK](/docs/holding/sdk-overview).
*Enhancements*
**OID4VCI manual redirect support**: Allows applications to implement the OID4VCI issuance workflow within embedded WebViews while maintaining full control over the redirect flow. Applications can now:
1. Call the new [`createAuthorizationSession(credentialOffer:clientId:)`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/create-authorization-session.html) method to initiate the flow.
This returns an `AuthorizationSession` object containing both the `authorizeUrl` and
`codeVerifier` properties.
2. Load the returned `authorizeUrl` in a WebView to handle user authentication and consent.
3. After successful authentication, capture the redirect using the configured `redirectUri`, then
extract the authorization code from the returned URL.
4. Complete the issuance workflow by calling the new
`retrieveCredentials(authorizationSession:authorizationCode:)` method, passing in the
[`AuthorizationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.issuance.dto/-authorization-session/index.html) and extracted authorization code.
*Bug fixes*
* Fixed an issue where closing the embedded browser during the OID4VCI authorization flow caused `retrieveCredentials` to hang indefinitely.
For complete details, refer to the
[full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/).
## OID4VCI manual redirect support and bug fixes in the iOS mDocs Holder SDK
2025-10-10
Tags: Holder, iOS
We have released a new version (v4.2.0) of the [iOS mDocs Holder SDK](/docs/holding/sdk-overview).
*Enhancements*
**OID4VCI manual redirect support**: Allows applications to implement the OID4VCI issuance workflow within embedded WebViews while maintaining full control over the redirect flow. Applications can now:
1. Call the new `createAuthorizationSession(credentialOffer:clientId:)` method to initiate the flow.
This returns an `AuthorizationSession` object containing both the `authorizeUrl` and
`codeVerifier` properties.
2. Load the returned `authorizeUrl` in a WebView to handle user authentication and consent.
3. After successful authentication, capture the redirect using the configured `redirectUri`, then
extract the authorization code from the returned URL.
4. Complete the issuance workflow by calling the new
`retrieveCredentials(authorizationSession:authorizationCode:)` method, passing in the
`AuthorizationSession` and extracted authorization code.
*Bug fixes*
* Fixed an issue where the passcode fallback button was not displayed when biometric authentication
failed and `UserAuthenticationType` was set to `.biometricOrPasscode`.
* Fixed an issue where calling `retrieveCredentials` before initializing the SDK did not correctly
throw the expected `MobileCredentialHolderError.sdkNotInitialized` error.
* Fixed an issue where calling `addCredential` with a credential that could not be verified against
any stored trusted issuer certificate incorrectly threw
`MobileCredentialHolderError.certificateNotFound` instead of
`MobileCredentialHolderError.invalidCredential`.
For complete details, refer to the
[full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/changelog).
## SDK EOL announcement
2025-10-07
Tags: Holder, Verifier Mobile, iOS, Android
In line with our SLA, the following SDK versions have now reached their **End of Life (EOL)** and
are no longer supported:
* iOS mDocs Holder SDK v3.
* iOS mDocs Verifier SDK v3.
* Android mDocs Holder SDK v3.
* Android mDocs Verifier SDK v3.
For a list of actively supported versions, please refer to the respective SDK documentation:
* [Holder SDKs](/docs/holding/sdk-overview#versions)
* [Verifier Mobile SDKs](/docs/verification/remote-mobile-verifiers/sdks/overview#versions)
If you have questions about upgrading or need support with migration, please
[get in touch](mailto:dev-support@mattr.global).
## Dependency upgrades and build fixes in the React Native Holder SDK
2025-10-06
Tags: Holder, React Native
We have released a new version (v12.1.0) of the
[React Native Holder SDK](https://api-reference-sdk.mattr.global/wallet-sdk-react-native/12.1.0/index.html).
*Enhancements*
* Upgraded SDK dependencies to remediate potential vulnerabilities.
*Bug fixes*
* Resolved build failures in applications using the SDK when the React Native New Architecture was
enabled.
## App-to-App verification support in the Android mDocs Verifier SDK
2025-09-30
Tags: Verifier Mobile, Android
We have released a new major version (v5.0.0) of the [Android mDocs Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview#android).
*New features*
* **Remote app verification support**: Introduced the `requestMobileCredentials` method to support
App-to-App verification flows.
* **Enhanced error visibility**: Detailed IACA validation errors are now surfaced during credential
verification, including specific errors for certificate expiry, validity periods, and trust chain
failures.
*Breaking changes*
* `TrustedIssuerCertificateNotFound` error is now returned when a matching trusted issuer
certificate is not found.
* The `MobileCredentialVerifier.initialize` method now accepts an additional optional
`platformConfiguration` parameter for MATTR VII tenant URL configuration.
This release also includes performance improvements for storage operations with large data sets and
bug fixes for NFC Device Engagement with Apple Wallet. For complete details, refer to the
[full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/).
*End of Life notice*
* In line with our SLA, major version `4.x.x` of the Android mDocs Verifier SDK has now entered its
**Maintenance** phase and will reach its **End of Life (EOL)** on **30 December 2025**, after
which it will no longer be supported.
## Authentication enhancements in the Android mDocs Holder SDK
2025-09-30
Tags: Holder, Android
We have released a new major version (v5.0.0) of the [Android mDocs Holder SDK](/docs/holding/sdk-overview#android).
*New features*
* **Customizable authentication prompts**: Authentication prompt title and description are now
exposed as overrideable string resources (`@string/global_mattr_unlock_storage_title` and
`@string/global_mattr_unlock_storage_description`).
*Breaking changes*
* JSON representation of `VerifierAuthenticationResult` now uses `result` instead of `type` for the
authentication result type.
*Bug fixes*
* Fixed issue where special characters in credential offers were escaped twice.
* Fixed incorrect exception handling in `OnlinePresentationSession.sendResponse` method.
* Resolved issue where `MobileCredentialHolder.addCredential` could throw incorrect exceptions
during trust chain evaluation failures.
This release also includes performance improvements for storage operations with large data sets. For
complete details, refer to the
[full SDK changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/).
*End of Life notice*
* In line with our SLA, major version `4.x.x` of the Android mDocs Holder SDK has now entered its
**Maintenance** phase and will reach its **End of Life (EOL)** on **30 December 2025**, after
which it will no longer be supported.
## Verifier Web SDK maintenance release
2025-09-16
Tags: Verifier Web
This release of the [Verifier Web SDK](/docs/verification/remote-web-verifiers/sdks/overview) (v2.1.0) is a
maintenance release. It includes code refactoring and improvements to support future capabilities.
## New React Native architecture support and spelling standardization in the React Native mDocs Holder SDK
2025-09-12
Tags: Holder, React Native
We have released a new version (v8.0.0) of the [React Native mDocs Holder SDK](/docs/holding/sdk-overview).
*Breaking changes*
* **React Native compatibility**: Added support for React Native’s [New Architecture](https://reactnative.dev/architecture/landing-page).
* The minimum supported React Native version is now `0.78.0`.
* **Spelling standardization (UK → US English)**: The SDK’s naming convention has been updated to use US English. This affects several methods and
error types:
* Methods:
* `initialise` → `initialize`
* `deinitialise` → `deinitialize`
* Errors:
* `MobileCredentialHolderError.AuthenticationCancelled` →\
`MobileCredentialHolderError.AuthenticationCanceled`
* `MobileCredentialHolderError.InvalidAuthorisationRequestUri` →\
`MobileCredentialHolderError.InvalidAuthorizationRequestUri`
* `MobileCredentialHolderError.InvalidAuthorisationRequestVerifiedByCertificate` →\
`MobileCredentialHolderError.InvalidAuthorizationRequestVerifiedByCertificate`
* `MobileCredentialHolderError.InvalidAuthorisationRequestVerifiedByDomain` →\
`MobileCredentialHolderError.InvalidAuthorizationRequestVerifiedByDomain`
* **Error handling consolidation**: The `getCredential` method now returns a new verification failure reason:
* `MobileCredentialVerificationFailureType.TrustedIssuerCertificateNotFound`,\
used when a credential cannot be verified due to a missing matched trusted issuer certificate.
*Enhancements*
* A new `msoHash` property has been added to the `MobileCredential` and
`MobileCredentialMetadata` types. This property represents a hashed Mobile Security Object, as defined in
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).\
It is distinct from the `id` property and should not be used in the `getCredential` method.
* **iOS platform improvements**: The SDK now enforces checks for empty CBOR arrays.\
Previously, credentials with an empty `IssuerNamespaces` field were accepted, but under the CDDL
specification in ISO/IEC 18013-5, this field must include at least one entry.\
This update ensures stricter standards compliance and improves interoperability.
*End of Life notice*
* In line with our SLA, version `7.0.0` of the React Native mDocs Holder SDK has now entered its
**Maintenance** phase and will reach **End of Life (EOL)** on **12 December 2025**, after which it
will no longer be supported.
## New React Native architecture support and spelling standardization in the React Native mDocs Verifier SDK
2025-09-12
Tags: Verifier Mobile, React Native
We have released a new version (v8.0.0) of the [React Native mDocs Verifier SDK](/docs/verification/sdks/overview).
*Breaking changes*
* **React Native compatibility**: Added support for React Native’s [New Architecture](https://reactnative.dev/architecture/landing-page).
* The minimum supported React Native version is now `0.78.0`.
* **Spelling standardization (UK → US English)**: The SDK’s naming convention has been updated to use US English. This affects the following methods:
* `initialise` → `initialize`
* `deinitialise` → `deinitialize`
* **Error handling consolidation**: Updates have been made to error handling in key methods:
* `createProximityPresentationSession`:
* The `onError` callback may now be invoked with:
* `MobileCredentialVerifierErrorType.FailedToCreateProximityPresentationSession` when a session
cannot be created.
* `MobileCredentialVerifierErrorType.UnknownError` when an unexpected exception occurs.
* The function may also return:
* `MobileCredentialVerifierErrorType.IllegalState` when receiving an unexpected function call
(for example, if no presentation session is established).
* `sendProximityPresentationRequest`:
* A new verification failure reason
`MobileCredentialVerificationFailureType.TrustedIssuerCertificateNotFound` is now returned when
a presented credential cannot be verified due to a missing matched trusted issuer certificate.
*Enhancements*
* **iOS platform improvements**: The SDK now enforces checks for empty CBOR arrays.\
Previously, credentials with an empty `IssuerNamespaces` field were accepted, but under the CDDL
specification in [ISO/IEC 18013-5](https://www.iso.org/standard/69084.html), this field must
include at least one entry.\
This update ensures stricter standards compliance and improves interoperability.
*End of Life notice*
* In line with our SLA, version `7.0.0` of the React Native mDocs Verifier SDK has now entered its
**Maintenance** phase and will reach **End of Life (EOL)** on **12 December 2025**, after which it
will no longer be supported.
## New React Native architecture support, spelling standardization and new features in the React Native Holder SDK
2025-09-12
Tags: Holder, React Native
We have released a new version (v12.0.0) of the [React Native Holder SDK](https://api-reference-sdk.mattr.global/wallet-sdk-react-native/latest/index.html).
*Breaking changes*
* **React Native compatibility**: Added support for React Native’s [New Architecture](https://reactnative.dev/architecture/landing-page).
* The minimum supported React Native version is now `0.78.0`.
* To use the New Architecture, update the @mattrglobal/pairing-crypto-rn peer dependency to version `0.4.4`.
* **Spelling standardization (UK → US English)**: The SDK’s naming convention has been updated to use US English. This affects methods, types, and
error definitions:
* Methods:
* `initialise` → `initialize`
* `AuthenticationCancelled` → `AuthenticationCanceled`
* Types:
* `InitialiseOptions` → `InitializeOptions`
* `AuthenticationCancelledError` → `AuthenticationCanceledError`
* Errors (mDocs):
* `MobileCredentialHolderError.AuthenticationCancelled` →\
`MobileCredentialHolderError.AuthenticationCanceled`
* `MobileCredentialHolderError.InvalidAuthorisationRequestUri` →\
`MobileCredentialHolderError.InvalidAuthorizationRequestUri`
* `MobileCredentialHolderError.InvalidAuthorisationRequestVerifiedByCertificate` →\
`MobileCredentialHolderError.InvalidAuthorizationRequestVerifiedByCertificate`
* `MobileCredentialHolderError.InvalidAuthorisationRequestVerifiedByDomain` →\
`MobileCredentialHolderError.InvalidAuthorizationRequestVerifiedByDomain`
*Features*
* **New property for credentials**: Added an `msoHash` property to the `MobileCredential` and `MobileCredentialMetadata` types.\
This property represents a hashed Mobile Security Object, as defined in [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).\
It is distinct from the `id` property and should not be used in the `credential.mobile.getCredential` method.
* **iOS platform improvements**: The SDK now enforces checks for empty CBOR arrays.\
Previously, credentials with an empty `IssuerNamespaces` field were accepted, but under the CDDL
specification in ISO/IEC 18013-5, this field must include at least one entry.\
This update ensures stricter standards compliance and improves interoperability.
*End of Life notice*
* In line with our SLA, version `11.0.0` of the React Native mDocs Holder SDK has now entered its
**Maintenance** phase and will reach **End of Life (EOL)** on **12 December 2025**, after which it
will no longer be supported.
## React Native compatibility and error handling changes in the React Native Verifier SDK
2025-09-12
Tags: Verifier Mobile, React Native
We have released a new version (v8.0.0) of the [React Native Verifier SDK](https://api-reference-sdk.mattr.global/verifier-sdk-react-native/latest/index.html).
*Breaking changes*
* **React Native compatibility**: The minimum supported React Native version is now `0.78.0`.
* React Native’s [New Architecture](https://reactnative.dev/architecture/landing-page) is not yet supported.
* **Spelling standardization (UK → US English)**: The SDK’s naming convention has been updated to use US English. This affected the following error type:
* Renamed `MobileCredentialVerifierErrorType.SdkNotInitialised` to `MobileCredentialVerifierErrorType.SdkNotInitialized`.
* **Error handling consolidation**: Updates have been made to error handling in key methods:
* `mobile.createProximityPresentationSession`:
* The `onError` callback may now be invoked with:
* `MobileCredentialVerifierErrorType.FailedToCreateProximityPresentationSession` when a session
cannot be created.
* `MobileCredentialVerifierErrorType.UnknownError` when an unexpected exception occurs.
* `mobile.sendProximityPresentationRequest`:
* A new verification failure reason
`MobileCredentialVerificationFailureType.TrustedIssuerCertificateNotFound` is now returned when
a presented credential cannot be verified due to a missing matched trusted issuer certificate.
*Enhancements*
* **iOS platform improvements**: The SDK now enforces checks for empty CBOR arrays.\
Previously, credentials with an empty `IssuerNamespaces` field were accepted, but under the CDDL
specification in [ISO/IEC 18013-5](https://www.iso.org/standard/69084.html), this field must
include at least one entry. This update ensures stricter standards compliance and improves interoperability.
*End of Life notice*
* In line with our SLA, version `7.0.0` of the React Native Verifier SDK has now entered its
**Maintenance** phase and will reach **End of Life (EOL)** on **12 December 2025**, after which it
will no longer be supported.
## iOS mDocs Holder 4.1.1 released
2025-09-03
Tags: Holder, iOS
This release fixed a rare crash that could occur when the
[initialize](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/initialize\(instanceid:userauthenticationconfiguration:credentialissuanceconfiguration:loggerconfiguration:\))
and
[deinitialize](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/deinitialize\(\))
methods were called concurrently.
## Verifier Web SDK 1.1.0 EOL announcement
2025-08-29
Tags: Verifier Web
In line with our SLA, the Verifier Web SDK v1.1.0 has now reached its **End of Life (EOL)** and is
no longer supported.
For a list of actively supported versions, please refer to the
[Verifier Web SDK documentation](/docs/verification/remote-web-verifiers/sdks/overview#versions).
If you have questions about upgrading or need support with migration, please
[get in touch](mailto:dev-support@mattr.global).
## SDK EOL announcement
2025-08-26
Tags: Holder, Verifier Mobile, iOS, Android
In line with our SLA, the following SDK versions have now reached their **End of Life (EOL)**: and
are no longer supported:
* iOS mDocs Holder SDK v2.0.0.
* iOS mDocs Verifier SDK v2.0.0.
* Android mDocs Holder SDK v2.0.0.
* Android mDocs Verifier SDK v2.0.0.
* React Native mDocs Verifier SDK v6.0.0.
For a list of actively supported versions, please refer to the respective SDK documentation:
* [Holder SDKs](/docs/holding/sdk-overview#versions)
* [Verifier Mobile SDKs](/docs/verification/remote-mobile-verifiers/sdks/overview#versions)
If you have questions about upgrading or need support with migration, please
[get in touch](mailto:dev-support@mattr.global).
## Improved certificate management and iOS data protection in the React Native Holder SDK
2025-08-19
Tags: Holder, React Native
We have released a new version (v11.0.0) of the React Native Holder SDK.
*Breaking changes*
* The SDK no longer supports credentials with the `BbsBlsSignature2020` proof type. Please ensure
issued credentials use supported alternatives.
*mDocs Enhancements*
* The SDK no longer checks the signature algorithm when adding verifier certificates. This aligns
with [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html), which does not specify
required algorithms for reader root certificates.
* RSA is now supported as a digital signature algorithm for Reader Authentication root certificates.
* On iOS, the SDK’s storage data protection class has been updated from Class C (Protected Until
First User Authentication) to Class B (Protected Unless Open), improving data availability while
maintaining platform security guarantees.\
See [Apple Platform Security](https://support.apple.com/en-nz/guide/security/secb010e978a/web) for
more on protection classes.
*Bug fixes*
* Fixed an issue where special characters in credential offers were escaped twice.
* Resolved a potential crash when establishing a BLE connection.
* Fixed an issue where the browser would retain focus after authenticating during credential
retrieval via OID4VCI.
* Fixed validation for ES384 and ES512 signatures.
*Other updates*
* Removed the `elliptic` dependency, previously used for signature format conversion. This
functionality is now handled by an internal implementation.
* Updated the peer dependency `react-native-cryptography` to version `2.1.2`.
*End of Life notice*
* In line with our SLA, version 10.0.0 of the React Native Holder SDK has now entered its
**Maintenance** phase and will reach **End of Life (EOL)** on **19 November 2025**, after which it
will no longer be supported.
## Improved iOS data protection and signature validation in the React Native Verifier SDK
2025-08-19
Tags: Verifier Mobile, React Native
We have released a new version (v7.2.0) of the React Native Verifier SDK.
*mDocs Enhancements*
* On iOS, the SDK’s storage data protection class has been updated from Class C (Protected Until
First User Authentication) to Class B (Protected Unless Open), improving data availability while
maintaining platform security guarantees.\
See [Apple Platform Security](https://support.apple.com/en-nz/guide/security/secb010e978a/web) for
more on protection classes.
*Bug fixes*
* Resolved a potential crash when establishing a BLE connection.
* Fixed validation for ES384 and ES512 signatures.
*Other updates*
* Updated the peer dependency `react-native-cryptography` to version `2.1.2`.
## New lifecycle management and improved iOS data protection in the React Native mDocs Verifier SDK
2025-08-19
Tags: Verifier Mobile, React Native
We have released a new version (v7.2.0) of the
[React Native mDocs Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview).
*Enhancements*
* Introduced a new `isInitialised` method to check whether the SDK is currently initialized.
* Added a `deinitialise` method to explicitly terminate the current presentation session and close
access to SDK storage.
* On iOS, the SDK’s storage data protection class has been updated from Class C (Protected Until
First User Authentication) to Class B (Protected Unless Open), improving data availability while
maintaining platform security guarantees.\
See [Apple Platform Security](https://support.apple.com/en-nz/guide/security/secb010e978a/web) for
more on protection classes.
*Bug fixes*
* Resolved a potential crash when establishing a BLE connection.
* Fixed validation for ES384 and ES512 signatures.
## New Pre-Authorized Code flow support and improved error handling in the React Native mDocs Holder SDK
2025-08-19
Tags: Holder, React Native
We have released a new version (v7.0.0) of the
[React Native mDocs Holder SDK](/docs/holding/sdk-overview).
*Breaking changes*
* The SDK now supports credential claiming using the
[OID4VCI Pre-Authorized Code Flow](/docs/issuance/pre-authorized-code/overview), enabling a
more seamless credential retrieval experience. This change introduces updates to several method
signatures and response structures:
* `retrieveCredentials` now accepts the original offer URL as a `String`, an optional
`transactionCode`, and no longer includes `autoTrustMobileIaca` or `redirectUri`, which are
now set in the `initialise` method.
* The following fields are now handled internally and no longer exposed from
`discoverCredentialOffer`:
* `authorizeEndpoint`
* `tokenEndpoint`
* `credentialEndpoint`
* `mdocIacasUri`.
* A new `TransactionCode` struct is returned by `discoverCredentialOffer`.
* The `initialise` method now supports user authentication configuration through a new
`UserAuthenticationConfiguration` object, replacing the previous `userAuthRequiredOnInitialise`
parameter. This allows developers to specify both **when** and **how** authentication is required,
including biometric-only or passcode options on iOS.
* Several error types have been consolidated to simplify handling:
* Errors from `discoverCredentialOffer` like `CredentialOfferNotFound` and
`IssuerMetadataServiceError` are now grouped under
`DiscoverCredentialOfferErrorType.FailedToDiscoverCredentialOffer`.
* Errors from `retrieveCredentials` like `AuthCodeNotFound` and `DeviceKeyGenerationError` are
replaced with broader categories such as `WebAuthenticationFailed` and
`FailedToDiscoverCredentialOffer`.
*Enhancements*
* Future-dated credentials are now supported via the `addCredential` method.
* The SDK no longer checks the signature algorithm when adding verifier certificates, aligning with
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
* On iOS, the SDK’s storage data protection class has been updated from Class C (Protected Until
First User Authentication) to Class B (Protected Unless Open), improving data availability while
maintaining platform security guarantees.\
See [Apple Platform Security](https://support.apple.com/en-nz/guide/security/secb010e978a/web) for
more on protection classes.
* RSA is now supported as a digital signature algorithm for Reader Authentication root certificates.
*Bug fixes*
* Fixed an issue where special characters in credential offers were escaped twice.
* Resolved a potential crash when establishing a BLE connection.
* Fixed an issue where the browser would retain focus after authenticating during credential
retrieval via OID4VCI.
* Fixed validation for ES384 and ES512 signatures.
*End of Life notice*
* In line with our SLA, version 6.0.0 of the React Native mDocs Holder SDK has now entered its
**Maintenance** phase and will reach **End of Life (EOL)** on **19 November 2025**, after which it
will no longer be supported.
## Bug fix in the Android Holder SDK
2025-08-12
Tags: Holder, Android
We have released new versions of the following Android SDKs:
*Android mDocs Holder SDK v4.1.1*
* Fixed an issue where special characters in credential offers were escaped twice.
* See the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/)
for full details.
*Android mDocs Verifier SDK v4.1.1*
* This release contains no functional changes, new features, or bug fixes beyond ensuring
interoperability with version 4.1.1 of the Android mDocs Holder SDK.
* See the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/)
for full details.
## IACA validation detailed errors surfacing, enhanced spec compliance and improved concurrency safety in the native SDKs
2025-08-12
Tags: Holder, Verifier Mobile, iOS, Android
We have released new versions of the following native SDKs with more detailed IACA validation errors
surfacing, enhanced spec compliance and improved concurrency safety:
*Android mDocs Holder SDK v5.0.0-rc.1*
* The authentication prompt's title and description are now exposed as
`@string/global_mattr_unlock_storage_title` and `@string/global_mattr_unlock_storage_description`.
Developers can customize these strings in their app's resource files.
* See the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/)
for full details.
*Android mDocs Verifier SDK v5.0.0-rc.1*
* When validating a credential's chain of certificates as part of credential verification, the SDK
now surfaces detailed errors to help identify the root causes of IACA validation failures. This
makes previously hidden issues more visible and enables developers to more effectively
troubleshoot integration issues and build clearer error feedback into user experiences. New
surfaced errors include:
* `TrustedIssuerCertificateNotFound`: When no matching trusted issuer certificate is found.
* `TrustedIssuerCertificateExpired`: When the matching trusted issuer certificate has expired.
* `TrustedIssuerCertificateNotYetValid`: When the matching trusted issuer certificate is not yet
valid.
* Fixed an issue with NFC device engagement with Apple Wallet running on iOS 26.
* See the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/)
for full details.
*iOS mDocs Holder SDK v4.2.0-rc.1*
* The SDK now enforces that the `IssuerNamespaces` field in credentials must contain at least one
entry, as required by the Concise Data Definition Language (CDDL) specification in ISO/IEC
18013-5. Previously, credentials with an empty `IssuerNamespaces` field were allowed. This update
improves standards compliance and interoperability by preventing empty CBOR arrays in this field.
* The SDK’s public data types now conform to `Sendable`, improving concurrency safety and ensuring
compatibility with Swift 6’s stricter requirements.
* See the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/changelog)
for full details.
*iOS mDocs Verifier SDK v5.0.0-rc.1*
* When validating a credential's chain of certificates as part of credential verification, the SDK
now surfaces detailed errors to help identify the root causes of IACA validation failures. This
makes previously hidden issues more visible and enables developers to more effectively
troubleshoot integration issues and build clearer error feedback into user experiences. New
surfaced errors include:
* `TrustedIssuerCertificateNotFound`: When no matching trusted issuer certificate is found.
* `TrustedIssuerCertificateExpired`: When the matching trusted issuer certificate has expired.
* `TrustedIssuerCertificateNotYetValid`: When the matching trusted issuer certificate is not yet
valid.
* The SDK now enforces that the `IssuerNamespaces` field in credentials must contain at least one
entry, as required by the Concise Data Definition Language (CDDL) specification in ISO/IEC
18013-5. Previously, credentials with an empty `IssuerNamespaces` field were allowed. This update
improves standards compliance and interoperability by preventing empty CBOR arrays in this field.
* The SDK’s public data types now conform to `Sendable`, improving concurrency safety and ensuring
compatibility with Swift 6’s stricter requirements.
* See the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/changelog)
for full details.
## Refined verifier certificates validation in the iOS and Android mDocs Verifier SDKs
2025-07-29
Tags: Verifier Mobile, iOS, Android
We have released new versions of the following native SDKs with refined verifier certificates
validation and miscellaneous bug fixes:
*Android mDocs Holder SDK v4.1.0*
* The SDK no longer checks the signature algorithm when adding verifier certificates. This aligns
with [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html), which does not specify
required algorithms for reader root certificates.
* Miscellaneous bug fixes and improvements:
* Resolved a potential crash when establishing a BLE connection.
* Fixed an issue where the browser would retain focus after authenticating during credential
retrieval via OID4VCI.
* Fixed validation for ES384 and ES512 signatures.
* Enabled using RSA as a digital signature algorithm for Reader Authentication root
certificates.
* See the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/)
for full details.
*Android mDocs Verifier SDK v4.1.0*
* Miscellaneous bug fixes and improvements:
* Resolved a potential crash when establishing a BLE connection.
* Fixed validation for ES384 and ES512 signatures.
* See the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/)
for full details.
*iOS mDocs Holder SDK v4.1.0*
* The SDK no longer checks the signature algorithm when adding verifier certificates. This aligns
with [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html), which does not specify
required algorithms for reader root certificates.
* Enabled using RSA as a digital signature algorithm for Reader Authentication root certificates.
* See the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/changelog)
for full details.
*iOS mDocs Verifier SDK v4.1.0*
* Resolved a potential crash when establishing a BLE connection.
* See the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/changelog)
for full details.
## Enhanced support for latest React Native versions in the React Native Mobile Credential Verifier SDK
2025-07-14
Tags: Verifier Mobile, React Native
We have released new versions of the following React Native SDKs:
*React Native mDocs Verifier SDK v7.1.0*
* Added support for React Native versions 0.77, 0.78, and 0.79.
* Resolved an issue where the incorrect callback was triggered when a proximity session was
terminated. Developers should now use the `onTerminated` callback instead of `onSessionTerminated`
for accurate session handling.
* Resolved issues related to Bluetooth permissions handling and session termination.
* Refer to the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/index.html#md:change-log)
for a complete description of the changes.
*React Native Verifier SDK v7.1.0*
* Added support for React Native versions 0.77, 0.78, and 0.79.
* Resolved an issue where the incorrect callback was triggered when a proximity session was
terminated. Developers should now use the `onTerminated` callback instead of `onSessionTerminated`
for accurate session handling.
* Resolved issues related to Bluetooth permissions handling and session termination.
* Refer to the
[SDK Docs change log](https://api-reference-sdk.mattr.global/verifier-sdk-react-native/latest/index.html#md:change-log)
for a complete description of the changes.
## New authentication options and mDoc Reader authentication in native SDKs
2025-07-07
Tags: Holder, Verifier Mobile, iOS, Android
We have released new versions of the following native SDKs with enhanced authentication controls and
support for mDoc Reader authentication:
*Android mDocs Holder SDK v4.0.0*
* Configure when biometric authentication is required, allowing more flexible user flows.
* Support for mDoc Reader authentication as defined in
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html), enabling holders to choose
whether to share credentials if the verifier cannot be authenticated.
* Ability to claim credentials with future effective dates.
* The Holder SDK now exposes the Mobile Security Object (MSO) hash in the `getCredential` and
`getCredentials` API responses. This immutable identifier simplifies record matching between the
Holder SDK and MATTR VII.
* The `deinitialize` method is now idempotent and non-throwing.
* See the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/)
for full details.
*Android mDocs Verifier SDK v4.0.0*
* The `deinitialize` method is now idempotent and non-throwing.
* See the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/)
for full details.
*iOS mDocs Holder SDK v4.0.0*
* Configure when biometric authentication is required for greater control over user interactions.
* Support for mDoc Reader authentication as defined in
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
* Ability to claim credentials with future effective dates.
* The Holder SDK now exposes the Mobile Security Object (MSO) hash in the `getCredential` and
`getCredentials` API responses. This immutable identifier simplifies record matching between the
Holder SDK and MATTR VII.
* Storage data protection class updated from C (Protected Until First User Authentication) to B
(Protected Unless Open). See
[Apple Platform Security forum](https://support.apple.com/en-nz/guide/security/secb010e978a/web)
for more information.
* See the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/changelog)
for full details.
*iOS mDocs Verifier SDK v4.0.0*
* The SDK can now request and verify a credential directly from an Apple Wallet using the
[Verify with Wallet API](https://developer.apple.com/wallet/get-started-with-verify-with-wallet/).
Only available for iOS 16 and above.
* New `initialized` read-only property to indicate SDK initialization state.
* New `deinitialize` method to terminate sessions and close SDK storage.
* Storage data protection class updated from C to B. See
[Apple Platform Security forum](https://support.apple.com/en-nz/guide/security/secb010e978a/web)
for more information.
* See the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/changelog)
for full details.
## Standalone React Native mDocs Holder SDK now available
2025-05-29
Tags: Holder, React Native
The new version (v6.0.0) of the React Native mDocs Holder SDK is now available as a standalone SDK.
This focused, lightweight evolution of our React Native Holder SDK offers a streamlined experience
purpose-built for handling mDocs.
Refer to the following resources for more information:
* [SDK Overview](/docs/holding/sdk-overview).
* SDK quickstart guides:
* [Claim a credential](/docs/holding/sdk-quickstart).
* [Remote presentation](/docs/holding/sdk-quickstart).
* [Proximity presentation](/docs/holding/sdk-quickstart).
* SDK tutorials:
* [Claim a credential](/docs/holding/credential-claiming-tutorial).
* [Remote presentation](/docs/holding/remote-presentation-tutorial).
* [Proximity presentation](/docs/holding/proximity-presentation-tutorial).
* [Reference SDK Docs](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/index.html).
## Support for aborting a session in the Verifier Web SDK
2025-05-29
Tags: Verifier Web
This release of the Verifier Web SDK (v2.0.2) introduces the following enhancements:
* The SDK now includes an `abortCredentialRequest` method for aborting the currently active session.
* In same-device flows, the browser window now closes automatically when the verification process
continues on a separate redirect page.
* When a response includes invalid credentials, the SDK no longer returns a
`PresentationFailureResult` error. Instead, it returns a `PresentationSuccessResult` that includes
details about any failed credentials and the reasons for their failure.
* The SDK now uses US English spelling conventions instead of UK English. See the
[SDK Docs change log](https://api-reference-sdk.mattr.global/verifier-sdk-web/latest/index.html#md:200)
for a full list of related changes.
* Consolidation of parameters passed to the `requestCredentials` method.
Refer to the
[SDK Docs changelog](https://api-reference-sdk.mattr.global/verifier-sdk-web/latest/index.html#md:200)
for more information.
## Pre-authorized Code support, Mobile app verification and NFC in-person verification now available in native SDKs
2025-05-26
Tags: Holder, Verifier Mobile, iOS, Android
We have released new versions of the following native SDKs:
*Android mDocs Holder SDK v3.0.0*
* The SDK now supports claiming credentials via the
[OID4VCI Pre-authorized Code flow](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-pre-authorized-code-flow).
* The SDK now uses US English spelling conventions instead of UK English. See the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/)
for a full list of related changes.
* Error handling consolidation.
* Refer to the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/)
for a complete description of new capabilities and breaking changes.
*Android mDocs Verifier SDK v3.0.0*
* The SDK now supports NFC device engagement.
* Simplification of proximity presentation state handling.
* The SDK now uses US English spelling conventions instead of UK English. See the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/)
for a full list of related changes.
* Refer to the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/)
for a complete description of new capabilities and breaking changes.
*iOS mDocs Holder SDK v3.0.0*
* The SDK now supports claiming credentials via the
[OID4VCI Pre-authorized Code flow](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-pre-authorized-code-flow).
* The SDK now uses US English spelling conventions instead of UK English. See the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/changelog)
for a full list of related changes.
* The SDK now supports the mac device authentication method.
* Error handling consolidation.
* Refer to the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/changelog)
for a complete description of new capabilities and breaking changes.
*iOS mDocs Verifier SDK v3.0.0*
* The SDK now supports
[mobile app verification](/docs/verification/remote-mobile-verifiers/workflow), allowing your
verifier application to request and verify an mDoc from another application installed on the same
iOS device.
* Simplification of proximity presentation state handling.
* The SDK now uses US English spelling conventions instead of UK English. See the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/changelog)
for a full list of related changes.
* Refer to the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/changelog)
## React Native Verifier SDKs release
2025-05-26
Tags: Verifier Mobile, React Native
We have released new versions of the following React Native SDKs:
*React Native mDocs Verifier SDK v7.0.0*
* Simplified handling of proximity presentation sessions.
* Support for NFC device engagement (Android devices only).
* Refer to the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/index.html#md:change-log)
for a complete description of new capabilities and breaking changes.
*React Native Verifier SDK v7.0.0*
* Simplified handling of mDocs proximity presentation sessions.
* Support for NFC device engagement (Android devices only) for mDocs proximity verification
sessions.
* The SDK can no longer be used to verify JSON credentials using the BBS 2020 proof type (i.e.
`BbsBlsSignature2020`).
* Removed the `elliptic` dependency, which was previously used for signature format conversion. This
functionality is now handled by an internal implementation.
* Refer to the
[SDK Docs change log](https://api-reference-sdk.mattr.global/verifier-sdk-react-native/latest/index.html#md:change-log)
for a complete description of new capabilities and breaking changes.
## Improved React Native SDKs compatibility with older iOS versions
2025-02-05
Tags: Holder, Verifier Mobile, React Native
We have released new versions of the following React Native SDKs:
*React Native Holder SDK v10.0.0*
* Applications embedding the SDK can now run on iOS 13 without crashing, as long as the SDK is not
initialized (the SDK features still require iOS 15 or later).
* Removed `statusInfo` from mobile credential presentation responses.
* Refer to the
[SDK Docs change log](https://api-reference-sdk.mattr.global/wallet-sdk-react-native/latest/index.html#md:change-log)
for a complete description of new capabilities and breaking changes.
*React Native Verifier SDK v6.0.0*
* Applications embedding the SDK can now run on iOS 13 without crashing, as long as the SDK is not
initialized (the SDK features still require iOS 15 or later).
* Refer to the
[SDK Docs change log](https://api-reference-sdk.mattr.global/verifier-sdk-react-native/latest/index.html#md:change-log)
for a complete description of new capabilities and breaking changes.
*React Native mDocs Verifier SDK v6.0.0*
* Applications embedding the SDK can now run on iOS 13 without crashing, as long as the SDK is not
initialized (the SDK features still require iOS 15 or later).
* Removed `statusInfo` from mobile credential presentation responses.
* [SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/index.html#md:change-log)
for a complete description of new capabilities and breaking changes.
## Native SDKs release
2025-02-05
Tags: Holder, Verifier Mobile, iOS, Android
We have released new versions of the following native SDKs:
*Android mDocs Holder SDK v2.0.0*
* The `statusInfo` property is no longer returned as part of verification results.
* Authorization requests for online presentation workflows must now include the `client_id` as a
subject alternative name record.
* Refer to the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/)
for a complete description of new capabilities and breaking changes.
*Android mDocs Verifier SDK v2.0.0*
* The `statusInfo` property is no longer returned as part of verification results.
* The `getTrustedIssuerCertificates` function now computes the
`TrustedCertificate.verificationResult` as well.
* Improved performance of `addTrustedIssuerCertificates` when adding certificates with revocation
lists.
* Refer to the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/)
for a complete description of new capabilities and breaking changes.
*iOS mDocs Holder SDK v2.0.0*
* The minimum deployment target is now set to iOS 13. This enables clients to support users up to
iOS 13, as long as the SDK is not initialized (The SDK functionality is only available for devices
from iOS 15 onwards).
* The `statusInfo` property is no longer returned as part of verification results.
* Authorization requests for online presentation workflows must now include the `client_id` as a
subject alternative name record.
* Refined error messages in the `createProximityPresentationSession` API.
* Refer to the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/changelog)
for a complete description of new capabilities and breaking changes.
*iOS mDocs Verifier SDK v2.0.0*
* The minimum deployment target is now set to iOS 13. This enables clients to support users up to
iOS 13, as long as the SDK is not initialized (The SDK functionality is only available for devices
from iOS 15 onwards).
* The `statusInfo` property is no longer returned as part of verification results.
* The `getTrustedIssuerCertificates` function now computes the
`TrustedCertificate.verificationResult` as well.
* Refined error messages in the `createProximityPresentationSession` API.
* Downloading status lists is now rate limited at 10 requests per second.
* Refer to the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/changelog)
## React Native SDKs now support mDocs revocation as well as using holder and verifier SDKs in a single application
2025-02-05
Tags: Holder, Verifier Mobile, React Native
We have released new versions of the following React Native SDKs:
*React Native Holder SDK v9.0.0*
* The SDK now supports checking the [revocation](/docs/issuance/revocation/overview) status of claimed mDocs.
* The SDK can now be used in the same application with compatible versions of the
[React Native mDocs Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview),
as detailed in the
[getting started](https://api-reference-sdk.mattr.global/wallet-sdk-react-native/latest/index.html#md:install-dependencies)
section.
* Refer to the
[SDK Docs change log](https://api-reference-sdk.mattr.global/wallet-sdk-react-native/latest/index.html#md:change-log)
for a complete description of new capabilities and breaking changes.
*React Native Verifier SDK v5.0.0*
* The SDK now supports checking the [revocation](/docs/issuance/revocation/overview) status of presented mDocs.
* The SDK can now be used in the same application with compatible versions of the React Native
Holder SDK, as detailed in the
[getting started](https://api-reference-sdk.mattr.global/verifier-sdk-react-native/latest/index.html#md:install-dependencies)
section.
* Refer to the
[SDK Docs change log](https://api-reference-sdk.mattr.global/verifier-sdk-react-native/latest/index.html#md:change-log)
for a complete description of new capabilities and breaking changes.
*React Native mDocs Verifier SDK v5.0.0*
* The SDK now supports checking the [revocation](/docs/issuance/revocation/overview) status of presented mDocs.
* The SDK can now be used in the same application with compatible versions of the React Native
Holder SDK, as detailed in the
[getting started](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/index.html#md:install-dependencies)
section.
* Refer to the
[SDK Docs change log](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/index.html#md:change-log)
for a complete description of new capabilities and breaking changes.
## Android mDocs SDKs now support using mDocs Holder and Verifier SDKs in a single application
2025-01-13
Tags: Holder, Verifier Mobile, Android
We have released new versions of the following Android SDKs:
*Android mDocs Holder SDK v1.1.0*
* The SDK can now be used in the same application with version 1.1.0 of the
[Android mDocs Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview).
*Android mDocs Verifier SDK v1.1.0*
* The SDK can now be used in the same application with version 1.1.0 of the
[Android mDocs Holder SDK](/docs/holding/sdk-overview).
## Android SDKs EOL announcement
2025-01-12
Tags: Holder, Verifier Mobile, Android
In line with our SLA, the following SDK versions have now reached their **End of Life (EOL)** and
are no longer supported:
* Android mDocs Holder SDK v4.
* Android mDocs Verifier SDK v4.
For a list of actively supported versions, please refer to the respective SDK documentation:
* [Holder SDKs](/docs/holding/sdk-overview#versions)
* [Verifier Mobile SDKs](/docs/verification/remote-mobile-verifiers/sdks/overview#versions)
If you have questions about upgrading or need support with migration, please
[get in touch](mailto:dev-support@mattr.global).
## Support for multiple verifier applications in the Verifier Web SDK
2025-01-08
Tags: Verifier Web
This release (v1.1.0) of the Verifier Web SDK introduces support for multiple verifier applications
interacting with a single MATTR VII tenant to perform
[online verification](/docs/verification/remote-web-verifiers/workflow) of [mDocs](/docs/concepts/mdocs).
To support this feature, the SDK can now be initialized with an `applicationId` to indicate to the
MATTR VII tenant what application the request is coming from.
Refer to the
[SDK Docs changelog](https://api-reference-sdk.mattr.global/verifier-sdk-web/latest/index.html#md:110)
for more information.
## Revocation and online presentation now available in first GA release of native mDocs Holder and Verifier SDKs
2024-12-02
Tags: Holder, Verifier Mobile, iOS, Android
We have released the first General Availability (GA) version of our iOS and Android mDocs Holder and
Verifier SDKs. Built around the most recent standards and best practices, these SDKs offer tools to
assist developers integrating credential holding, presentation and verification capabilities into
their native applications.
*Holder SDKs key features*
Both the iOS (v1.0.1) and Android (v1.0.1) mDocs Holder SDKs support a similar set of capabilities:
* **Claim an mDoc**
* Interact with a credential offer and claim an mDoc as per
[OID4VCI (OpenID for Verifiable Credential Issuance)](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html).
* Manage a list of trusted issuers which mDoc offers can be validated against.
* Store claimed mDocs and manage access to that storage.
* Manage access to device keys which are bound to issued mDocs.
* Use referenced Status lists to check mDocs' [revocation status](/docs/issuance/revocation/overview).
* **Present an mDoc**
* Present a claimed mDoc for verification via a
[proximity presentation workflow](/docs/verification/in-person-overview) as per
[ISO 18013-5](https://www.iso.org/standard/69084.html).
* Present a claimed mDoc for verification via a
[remote (online) presentation workflow](/docs/verification/remote-overview) as per
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html).
Refer to the following pages for more information:
* iOS Holder SDK:
* [Overview](/docs/holding/sdk-overview)
* [Claim a credential tutorial](/docs/holding/credential-claiming-tutorial)
* [Proximity presentation tutorial](/docs/holding/proximity-presentation-tutorial)
* [SDK Reference Docs](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk)
* Android Holder SDK:
* [Overview](/docs/holding/sdk-overview)
* [SDK Reference Docs](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/)
*Verifier SDKs key features*
Both the iOS (v1.0.1) and Android (v1.0.0) mDocs Verifier SDKs support a similar set of
capabilities:
* **Verify an mDoc**:
* Request and verify mDocs via a proximity presentation workflow as per
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
* Manage a list of trusted issuer certificates which presented mDocs can be validated against.
* Manage a list of mDocs status lists which is used to check the
[revocation status](/docs/issuance/revocation/overview).
Refer to the following pages for more information:
* iOS Verifier SDK:
* [Overview](/docs/verification/remote-mobile-verifiers/sdks/overview)
* [Proximity verification tutorial](/docs/holding/proximity-presentation-tutorial)
* [SDK Reference Docs](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk)
* Android Verifier SDK:
* [Overview](/docs/verification/remote-mobile-verifiers/sdks/overview)
* [SDK Reference Docs](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/)
To get started with any of these SDKs, please [contact
us](mailto:sales@mattr.global) so we can work together to find the best
solution for you.
## New versions of React Native SDKs
2024-11-15
Tags: Holder, Verifier Mobile, React Native
We have released new versions of the following SDKs:
*React Native Holder SDK*
This new major version (v8.0.0) introduces the following changes:
* Introduced support for online presentation of mDocs via OID4VP as per ISO/IEC 18013-7:2025.
* Replacing Realm database with a custom storage system for all mDocs data.
* Various enhancements and refinements to existing SDK methods.
* Minimum supported React Native version is now v0.73.
Refer to the
[SDK Docs changelog](https://api-reference-sdk.mattr.global/wallet-sdk-react-native/latest/index.html#md:change-log)
for a detailed description of the changes.
*React Native Verifier SDK*
This new major version (v4.0.0) introduces the following changes:
* Replacing Realm database with a custom storage system for all mDocs data.
* Various enhancements and refinements to existing SDK methods.
* Minimum supported React Native version is now v0.73.
Refer to the
[SDK Docs changelog](https://api-reference-sdk.mattr.global/verifier-sdk-react-native/latest/index.html#md:change-log)
for a detailed description of the changes.
*React Native mDocs Verifier SDK*
This new major version (v4.0.0) introduces the following changes:
* Replacing Realm database with a custom storage system for all mDocs data.
* Various enhancements and refinements to existing SDK methods.
* Minimum supported React Native version is now v0.73.
Refer to the
[SDK Docs changelog](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/index.html#md:change-log)
for a detailed description of the changes.
## Terminology updates across MATTR platforms
2024-10-15
Tags: Holder, Verifier Mobile, Verifier Web, iOS, Android, React Native
To make it easier to consume our capabilities, we have made some changes to our terminology to
describe credentials supported by MATTR platforms by their underlying technology and standards. The
following credential formats are supported:
* [mDocs](/docs/concepts/mdocs): Previously referred to as Mobile credentials.
* [CBOR Web Tokens (CWT) credentials](/docs/concepts/cwt): Previously referred to as Compact credentials.
* JSON credentials: Previously referred to as Web credentials.
## New Verifier Web SDK enables online verification of Mobile Credentials
2024-10-01
Tags: Verifier Web
Our new [Verifier Web SDK](/docs/verification/remote-web-verifiers/sdks/overview) (v1.0.0) is now available, integrating with an existing MATTR VII tenant to
easily add online verification capabilities into your web applications. Using this SDK your web
applications can now securely verify mDocs presented via both
same-device and cross-device flows, as per [ISO/IEC 18013-7](https://www.iso.org/standard/91154.html) and
[OpenID for Verifiable Presentations (OID4VP)](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html).
### Features [#features]
* Compliant with [ISO/IEC 18013-7](https://www.iso.org/standard/91154.html) and
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html).
* Supports both same-device and cross-device flows.
* Simple integration into existing web applications.
### Additional information [#additional-information]
* Refer to the
[Verifier Web SDK Docs](https://api-reference-sdk.mattr.global/verifier-sdk-web/latest/index.html)
for a detailed description of this new SDK.
* Refer to the Docs section to learn more about the
[online verification flow](/docs/verification/remote-overview) and its
[implementation](/docs/verification/remote-web-verifiers/workflow).
## Refined error handling and extended features in the React Native Mobile Credential Verifier SDK
2024-09-17
Tags: Verifier Mobile, React Native
A new major version (v3.0.0) of the React Native Mobile Credential Verifier SDK is now available,
offering refined error handling, extended features and several bug fixes.
### Breaking changes [#breaking-changes]
* The `createProximityPresentationSession` method now returns the
`MobileCredentialVerifierErrorType.BluetoothDisabled` error when Bluetooth is powered off.
* The `sendProximityPresentationRequest` method now returns the
`MobileCredentialVerifierErrorType.SessionTerminated` error when the current proximity
presentation session is terminated.
* The `onSessionTerminated` callback function for `createProximityPresentationSession` method can
now be invoked with the `ProximityPresentationSessionTerminationError.ResponseNotReceived` error
when a Mobile Credential response was not received from holder.
### Features [#features]
The SDK now supports:
* Additional extended key usage in a document signer certificate.
* Clock skew tolerance during verification.
* React Native 0.72.14.
### Bug Fixes [#bug-fixes]
* To mitigate a potential race condition issue, we have improved the process of generating new
encryption keys.
#### iOS specific [#ios-specific]
* Fixed an issue where Bluetooth sessions might have been terminated before data exchange was
completed. This could have occurred when the developer terminates the session after receiving a
single response, but that response doesn't include all the required data. This might happen on
devices with lower data transmission speed or when the response size is too big.
#### Android specific [#android-specific]
* Fixed an issue where the SDK was unable to handle Mobile Credential presentation responses with
floating point claims.
## Support for React Native 0.72.14 and local secure store in the React Native Verifier SDK
2024-09-17
Tags: Verifier Mobile, React Native
A new minor version (v3.1.0) of the React Native Verifier SDK is now available, introducing support
for React Native 0.72.14 as well as local secure store implementation.
### Features [#features]
The SDK now supports:
* React Native 0.72.14.
* Local secure store implementation. Peer dependency `react-native-secure-key-store` is no longer
required, but you must include `@mattrglobal/react-native-cryptography@2.0.0` in your application
dependencies.
## Improved encryption key generation in the React Native Wallet SDK
2024-08-09
Tags: Holder, React Native
The new version (v7.0.1) of our React Native Wallet SDK is now available, improving the process of
generating new encryption keys to mitigate a potential race condition issue.
Note that when consuming this new version of the SDK, you must upgrade your application dependencies
to include `@mattrglobal/react-native-cryptography@2.0.0`.
Refer to the [MATTR Pi React Native Wallet SDK Docs](https://api-reference-sdk.mattr.global/wallet-sdk-react-native/latest/index.html#md:change-log) for a detailed description of the new features and changes included in this release.
## Enhanced security in the MATTR Pi Wallet SDK
2024-06-20
Tags: Holder, React Native
The latest version (v7.0.0) of our MATTR Pi Wallet SDK is out, providing default configurations that
enhance security around JSON-LD contexts.
### Breaking Changes [#breaking-changes]
* Unknown JSON-LD contexts are now invalid by default.
* In-line JSON-LD contexts definitions are now disabled by default.
When using the default configuration, you must now manually whitelist any referenced JSON-LD
contexts, and remove any in-line JSON-LD context definitions.
### Key Features [#key-features]
* Introducing support for React Native (RN) 0.72.0.
* Miscellaneous bug fixes.
Refer to the [MATTR Pi Wallet SDK Docs](https://api-reference-sdk.mattr.global/wallet-sdk-react-native/latest/index.html#md:change-log)
for a detailed description of the new features and changes included in this release.
## New capabilities in the MATTR Pi Verifier SDK
2024-04-05
Tags: Verifier Mobile, React Native
The latest version (v3.0.0) of our MATTR Pi Verifier SDK is out, making more capabilities available
for developers to build into their applications.
#### Key Features [#key-features]
* Introducing [Mobile Credentials](/docs/concepts/mdocs) capabilities via an extension:
* Manage a list of trusted issuer certificates which presented Mobile Credentials can be
validated against.
* Interface with a Mobile Credential holder to request presentations of issued Mobile
Credentials as per [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
* Verify incoming Mobile Credentials presentations.
* Introducing [Ecosystem](/docs/digital-trust-service) capabilities via an extension:
* Confirm the verified credential is valid in the ecosystem.
* Confirm the verified credential was issued by a valid ecosystem issuer.
#### Breaking changes [#breaking-changes]
* Two initialization parameters have been renamed to maintain consistency across credential profiles
(without affecting their functionality):
* The `assertNotBefore` parameter has been renamed to `assertValidFrom`.
* The `assertExpiry` parameter has been renamed to `assertValidUntil`.
Refer to the [MATTR Pi Verifier SDK Docs](https://api-reference-sdk.mattr.global/verifier-sdk-react-native/latest/index.html)
for a detailed description of the new features and changes included in this release.
## New functionalities and improved error handling in the MATTR Pi Wallet SDK
2024-01-31
Tags: Holder, React Native
We are releasing a new version (v5.0.0) of our MATTR Pi Wallet SDK.
#### Key features [#key-features]
* Introducing Android support for [Mobile Credentials](/docs/concepts/mdocs) capabilities via the
@mattrglobal/mobile-credential-holder-react-native extension integration. Supported on both iOS
and Android.
* Introducing support for [Ecosystem](/docs/digital-trust-service) capabilities via the
@mattrglobal/ecosystem-sdk-react-native extension integration.
* Remote DID document and context resolution is now cached to prevent unnecessary requests and
improve overall performance.
* Bug fixes and overall performance enhancements. Refer to the
[SDK Docs change log](https://api-reference-sdk.mattr.global/wallet-sdk-react-native/latest/index.html#md:change-log)
for more details.
#### Breaking changes [#breaking-changes]
This release introduces several changes to error handling that should be handled as breaking
changes. Refer to the
[SDK Docs change log](https://api-reference-sdk.mattr.global/wallet-sdk-react-native/latest/index.html#md:change-log)
for a complete description of the new errors and how they might affect your implementation.
## Enhancements and new features across MATTR platforms
2023-07-07
Tags: Holder, React Native
We're excited to announce the following updates and new features across our MATTR platforms:
* You can now issue Compact Credentials using the OpenID Credential Provisioning flow. This includes
using all the features that this flow enables, such as integration hooks, claims source
integration and multi-credential issuance for Compact Credentials.
* We've enabled the MATTR Pi Wallet Toolkit with the capability to retrieve and hold Compact
Credentials.
* We now support multiple key types within a single DID, which means you can issue credentials in
multiple Credential Profiles using the same DID.
## Introducing MATTR Pi: Our flexible tools for creating apps your users will love
2023-02-22
Tags: Holder, Verifier Mobile, React Native
We are excited to announce the official launch of MATTR Pi - our SDK-centric platform encompassing
toolkits with the flexibility to build solutions that work for you.
Whilst pi (π) is a mathematical constant, its decimals are infinite. All MATTR Pi toolkits have our
rigorous commitment to international standards and leading security practices baked in, but they
equip you with the limitless potential to create value in this new world of digital trust.
The foundations of MATTR Pi, available now, are the MATTR Pi Wallet Toolkit and the MATTR Pi Compact
Credentials Verifier Toolkit.
* The Wallet Toolkit includes all you need to get started creating your own digital wallet
experiences quickly.
* The Compact Credentials Verifier Toolkit lets you easily integrate verification capabilities into
any existing or new application.
Interested in creating a verifier or wallet solution and not sure where to start?
[Get in touch](mailto:dev-support@mattr.global) with us today to discuss the best option for your
business.
## Compact Credential Verifier SDK
2022-07-07
Tags: Verifier Web
We’ve transformed our Credential Verification capabilities to support the wider platform by making
it work on your own mobile experience or integrating them into other types of your applications.
Utilizing our SDK will significantly reduce your development time while ensuring you are leveraging
safe and reliable code libraries.
The following capabilities & benefits are provided in the Verifier SDK:
* Build your own Compact Credential verification solution into existing applications using the same
tools as the MATTR Verifier App.
* Validate Compact and Semantic Compact Credentials' authenticity.
* Offline verification of your trusted credentials.
* Refresh cached revocation lists and trusted issuers.
* Privacy-preserving features.
* Ongoing support for fast-evolving standards of digital trust and verifiable data.
* Create both iOS and Android apps using the same codebase.
Interested in learning more about how you might use the MATTR Verifier SDK?
[Get in touch](mailto:dev-support@mattr.global) with us today.
# MATTR VII Changelog
URL: /docs/resources/changelog/vii
[Subscribe](/docs/resources/changelog/newsletter-signup) to our release
newsletter to get the latest updates on product releases, new features and bug
fixes.
## Supported versions [#supported-versions]
Below are supported versions of the MATTR VII Platform, including the current active version,
supported versions, expected end-of-life (EOL) dates and reference documentation.
| Major version | Status | Minor version | End of Life date | Documentation |
| ------------- | ------ | ------------- | ---------------- | ------------------------------------------------------------------------------------------------- |
| v12 | Latest | 12.19.0 | - | [MATTR Platform 12.19.0 API Reference](/docs/api-reference) |
| v12 | Active | 12.18.0 | - | [MATTR Platform 12.18.0 API Reference](https://release-platform-12-18-0.learn.mattrarchives.com/) |
| v12 | Active | 12.17.0 | - | [MATTR Platform 12.17.0 API Reference](https://release-platform-12-17-0.learn.mattrarchives.com/) |
## MATTR VII Management API maintenance release
2026-07-01
Tags: Maintenance, Platform Management
This release of the MATTR VII Management API (v1.33.2) is a maintenance release. It includes internal
changes to enhance performance and stability.
## RICAL and 3-tier PKI support in MATTR VII
2026-07-01
Tags: Verification, Trust Networks
This release of the MATTR VII Platform (v12.19.0) introduces the following changes:
* **RICAL support**: MATTR VII now supports RICALs (Reader Identity Certificate Authority Lists),
the counterpart to a VICAL for verifier trust. Where a VICAL distributes trusted mDoc issuer
roots, a RICAL collects and validates Reader Root Certificates from relying parties and signs
them into a single list that holder applications (wallets) can consume. When a wallet trusts a
RICAL, it can authenticate any verifier whose certificate chain anchors to a root included in the
list, without maintaining a direct trust relationship with each individual verifier. To learn more,
refer to the [RICAL overview](/docs/digital-trust-service/rical-overview),
the [RICAL guide](/docs/digital-trust-service/rical-guide), and the
[RICAL consumption](/docs/digital-trust-service/rical-consumption) page.
* **3-tier PKI model support**: The Digital Trust Service now supports a 3-tier certificate model in
addition to the existing 2-tier model when signing trusted lists. The 3-tier model introduces a DTS
intermediate CA certificate between the DTS root CA and the signer certificate (VICAL Signer
Certificate or RICAL Signer Certificate), letting you keep the DTS root CA private key offline and
delegate day-to-day signing to the intermediate CA. The 3-tier model is supported for unmanaged
(external) DTS certificates. To learn more, refer to the
[DTS certificates overview](/docs/digital-trust-service/certificates-overview).
* **Link certificate support**: MATTR VII now supports maintaining trust continuity for DTS
participant issuer and verifier certificates via link certificates. When creating an issuer or
verifier certificate, you can add it as a successor to a previously uploaded certificate by
providing the predecessor's identifier and a link certificate that ties the two together. This
preserves trust for relying parties and wallets across a certificate rotation without them first
having to consume an updated VICAL or RICAL. To learn more, refer to linked certificates in the
[VICAL overview](/docs/digital-trust-service/vical-overview#linked-certificates) and the
[RICAL overview](/docs/digital-trust-service/rical-overview#linked-certificates).
## RICAL trust lists and externally managed DTS certificate chains in the MATTR Portal
2026-07-01
Tags: Platform Management, Trust Networks
This release of the MATTR Portal (v1.79.1) introduces the following enhancements:
* **RICAL trust list support**: Extends MATTR DTS capabilities by adding support for generating and
publishing RICAL (Reader Identity Certificate Authority List) trust lists. To learn more, refer to
the [RICAL overview](/docs/digital-trust-service/rical-overview) and the
[RICAL guide](/docs/digital-trust-service/rical-guide).
* **Externally managed DTS certificate chains**: Introduces the capability to configure an externally
managed DTS certificate chain with optional intermediate certificates. To learn more, refer to the
[DTS certificates overview](/docs/digital-trust-service/certificates-overview).
* **Trust continuity via link certificates**: Adds support for maintaining DTS participant issuer and
verifier trust continuity via link certificates. When adding an issuer or verifier certificate, you
can now link it to a previously uploaded certificate as a successor. To learn more, refer to linked
certificates in the [VICAL overview](/docs/digital-trust-service/vical-overview#linked-certificates)
and the [RICAL overview](/docs/digital-trust-service/rical-overview#linked-certificates).
## Holder and Verifier SDK Tethering support in MATTR VII
2026-06-22
Tags: Platform Management, Holding, Verification
This release of the MATTR VII Platform (v12.18.0) introduces the following changes:
* **Holder and Verifier SDK Tethering**: MATTR VII now supports SDK Tethering for both the MATTR
Holder SDKs and the MATTR Verifier SDKs. Tethering registers each app instance with your MATTR VII
tenant, establishing a trusted relationship between the SDK and the platform that underpins
capabilities such as Wallet Attestation. Refer to the
[SDK Tethering](/docs/holding/sdk-operations/sdk-tethering) guide and the
[Holder SDK overview](/docs/holding/sdk-overview) for Holder SDK details, and the
[Verifier SDK overview](/docs/verification/remote-mobile-verifiers/sdks/overview) for the Verifier
SDKs.
* **Deprecation of the authenticated Status list retrieval endpoints**: The following authenticated
endpoints are now deprecated. In line with our SLA, they will be removed in a release at least 90
days from this announcement:
* `GET /v2/credentials/mobile/status-lists` (Retrieve all Status lists)
* `GET /v2/credentials/mobile/status-lists/{statusListId}` (Retrieve a Status list)
These endpoints are superseded by the existing public Status list endpoints. To discover the
Status lists available on a tenant, use the
[Status list distribution](/docs/api-reference/platform/status-list-retrieval/getStatusListDistribution)
endpoint (`GET /v2/credentials/mobile/status-lists/distribution`). To retrieve an individual Status
list token, use the
[Retrieve a Status list token](/docs/api-reference/platform/status-list-retrieval/getStatusListToken)
endpoint (`GET /v2/credentials/mobile/status-lists/{statusListId}/token`). We recommend migrating
any integrations away from the deprecated endpoints before the end of the deprecation window.
## MATTR VII Management API maintenance release
2026-06-22
Tags: Maintenance, Platform Management
This release of the MATTR VII Management API (v1.33.1) is a maintenance release. It includes internal changes to enhance performance and stability.
## Global logout and consistent certificate pages in the MATTR Portal
2026-06-08
Tags: Platform Management
This release of the MATTR Portal (v1.78.1) introduces the following enhancements:
* **Global logout support**: Logging out now ends all of a user's Portal sessions across every
device, rather than only the current session, and revokes any tokens issued to that user
(note that client credentials are not affected by logouts).
* **Consistent certificate pages**: The certificate upload preview and certificate details pages
have been updated across all certificate types for a more consistent user experience.
## Global logout support in MATTR VII Management API
2026-06-08
Tags: Platform Management
This release of the MATTR VII Management API (v1.33.0) introduces the following enhancement:
* **Global logout support**: Logging out from the Portal now ends all of a user's Portal sessions across every device, rather than only the current session, and revokes any issued tokens. This is enabled by a new endpoint offered by the Management API.
## Localized claim labels, larger pre-authorized offers, and user deletion changes in MATTR VII
2026-06-08
Tags: Issuance, Verification
This release of the MATTR VII Platform (v12.17.0) introduces the following enhancements:
* **Localized claim labels**: Issuers can now define optional translations of their claim names in their credential configurations. These localized labels are published to the issuer metadata at the `/.well-known/openid-credential-issuer` endpoint, so wallet apps can discover them and render claim names in the holder's preferred language. Localized labels are currently supported for mDoc credential configurations. Refer to [Localized display labels](/docs/issuance/credential-configuration/overview#localized-display-labels) and the [credential configuration guide](/docs/issuance/credential-configuration/guide#credential-content) for more information.
* **Larger Pre-authorized Code offer payloads**: The maximum payload size for Pre-authorized Code credential offers has been increased from 100KB to 500KB. Refer to [How to create an OID4VCI credential offer](/docs/issuance/credential-offer/guide#generate-an-offer-uri) for best practices on payload size when delivering credentials over BLE compared with remote presentation.
* **User deletion now invalidates issued credentials**: When a user is deleted, all of their data is removed and any credential previously issued to them is no longer valid. Refer to [Users](/docs/issuance/users/overview) for more information.
* **Session correlation with `state`**: Verifier web applications can now pass an optional opaque `state` value to `requestCredentials()` and receive it back in the result, letting them attach their own correlation reference to a presentation session without implementing separate state management. MATTR VII threads the value through the OpenID4VP presentation flow and returns it on both successful and failed results, in both same-device and cross-device flows. Refer to [Correlating verification sessions with your system](/docs/verification/remote-web-verifiers/guides/correlating-verification-sessions) for more information.
## Participants and Trust Lists split, simplified VICAL creation, and DC API testing in the MATTR Portal
2026-05-26
Tags: Platform Management, Trust Networks, Verification
This release of the MATTR Portal (v1.77.0) introduces the following enhancements:
* **Participants and Trust Lists pages**: To simplify the interface and prepare for upcoming
digital trust service features, the previous Ecosystem page has been split into two dedicated
sections under *Digital Trust Service*: **Participants** (for managing issuer participants and
their IACA certificates) and **Trust Lists** (which hosts the **VICAL (Trusted issuers)** tab
where you configure and publish VICALs). The Ecosystem name/ID box and edit/delete modals have
been removed from these pages. The **Ecosystem** page is now only available for the initial
one-time ecosystem creation. Once an ecosystem exists, the page is no longer shown and all
subsequent management happens through the Participants and Trust Lists pages.
* **Simplified VICAL creation**: Removed the Format field and consolidated all VICAL configuration
into a single collapsible card.
* **DC API support in the web verifier Test modal**: The Test modal for web verifier applications
now supports the [Digital Credentials API](/docs/verification/remote-web-verifiers/dc-api/overview) (DC API)
as well.
## MATTR VII Platform maintenance release
2026-05-25
Tags: Maintenance
This release of the MATTR VII Platform API (v12.16.0) is a maintenance release. It includes internal
changes to support upcoming features.
## Token revocation in MATTR VII Management API
2026-05-25
Tags: Platform Management
This release of the MATTR VII Management API (v1.32.0) introduces the following enhancement:
* **Token revocation for Management API**: Administrators can now immediately revoke a user's
access to the Management API, ensuring that compromised or offboarded users lose access without
waiting for their tokens to expire. Access can be restored at any time, and revocation does not
prevent the user from signing in again if they remain authorized.
## OpenID4VP V1 support for DC API web verification in MATTR VII
2026-05-12
Tags: Verification
This release of the MATTR VII Platform (v12.15.0) introduces the following enhancements:
* **OpenID4VP V1.0 alignment for DC API**: The tech preview implementation of the [Digital Credentials API](https://wicg.github.io/digital-credentials/) (DC API) for web verification is now aligned with [OpenID4VP V1.0](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html). This update ensures that credential presentation requests via the DC API conform to the latest OpenID for Verifiable Presentations specification.
* **Expanded claims source mapping parameters**: Claims source configurations can now map from a new `wallet` object (exposing `wallet.id` and `wallet.instanceId`) and from a new `issuanceProtocol` parameter that identifies the protocol used to issue the credential (currently `openid4vci`). The existing `client` object (`client.instanceId`) remains available for backwards compatibility but is being phased out. Update existing configurations to map from `wallet.instanceId` instead. Refer to the [claims source overview](/docs/issuance/claims-source/overview) for more information.
## MATTR VII Management API maintenance release
2026-05-11
Tags: Maintenance, Platform Management
This release of the MATTR VII Management API (v1.31.0) is a maintenance release. It includes internal changes to enhance performance and stability.
## Compressed access tokens and certificate details in the MATTR Portal
2026-04-29
Tags: Platform Management
This release of the MATTR Portal (v1.76.0) introduces the following enhancements:
* **Compressed access token support**: The Portal was upgraded to handle compressed access tokens used by the [Management API v1.30.0](#token-compression-and-authorization-cache-enhancements-in-mattr-vii-management-api-2026-04-28).
* **Certificate detail section**: Added a Certificate detail section to all certificate types, which displays certificate details in a human-readable format.
## MATTR VII Platform maintenance release
2026-04-28
Tags: Maintenance
This release of the MATTR VII Platform API (v12.14.0) is a maintenance release. It includes internal changes to support upcoming features.
## Token compression and authorization cache enhancements in MATTR VII Management API
2026-04-28
Tags: Platform Management
This release of the MATTR VII Management API (v1.30.0) introduces the following enhancements:
* **Access token compression**: Permissions are now compressed when issuing MATTR VII access tokens. This prevents exceeding the HTTP request header payload size limit (16KB), which would otherwise result in an HTTP 431 status code (Request Header Fields Too Large).
* **Authorization cache reliability**: Enhanced reliability of the internal authorization module cache.
## DC API verification and claim sources scope in MATTR Portal
2026-04-16
Tags: Platform Management, Issuance, Verification
This release of the MATTR Portal (v1.75.0) introduces the following enhancements:
* **DC API Verifier Applications**: You can now use the MATTR Portal to create Android and Web Verifier Applications that use the Digital Credentials API (DC API) to request and verify credentials remotely.
* **Claim sources scope configuration**: You can now add a `scope` field to the OAuth client credentials authorization configuration for claim sources. This supports integration with data servers that require the resource identifier to be passed as a `scope` rather than an `audience` parameter. Refer to the [claims source API reference](/docs/issuance/claims-source/api-reference#configure-a-claims-source) for more information.
## Claim sources OAuth scope configuration in MATTR VII
2026-04-13
Tags: Issuance
This release of the MATTR VII platform (v12.13.0) introduces the following enhancement:
* **OAuth scope parameter for claim sources**: The OAuth client credentials authorization configuration for claim sources now supports an optional `scope` field. This enables integration with data servers that require the resource identifier to be passed as a `scope` rather than an `audience` parameter. Existing claim sources are unaffected by this change. Refer to the [claims source API reference](/docs/issuance/claims-source/api-reference#configure-a-claims-source) for more information on how to use this new parameter.
## Maintenance update in MATTR VII Management API
2026-04-13
Tags: Maintenance, Platform Management
This release of the MATTR VII Management API (v1.29.3) is a maintenance release. It includes internal changes to enhance performance and stability.
## Credential reports in the MATTR Portal
2026-04-01
Tags: Platform Management, Issuance
This release of the MATTR Portal (v1.74.0) introduces the following enhancements:
* **Credential reports**: You can now generate reports of credential lifecycle operations across your tenant, broken down by day, credential profile, and credential type. Reports cover issuance events and status changes (such as revocations) for CWT and mDoc credentials. Refer to [Credential reports](/docs/issuance/credential-reports/guide) for more information.
* **UX enhancements**: Miscellaneous UX and display improvements.
## Credential reports in MATTR VII
2026-03-30
Tags: Issuance
This release of the MATTR VII platform (v12.12.0) introduces the following enhancements and fixes:
* **Credential reports**: You can now generate reports of credential lifecycle operations across your tenant, broken down by day, credential profile, and credential type. Reports cover issuance events and status changes (such as revocations) for CWT and mDoc credentials. Refer to [Credential reports](/docs/issuance/credential-reports/guide) for more information.
* **Bug fixes**: Miscellaneous bug fixes and stability improvements.
## Maintenance update in MATTR VII Management API
2026-03-30
Tags: Maintenance, Platform Management
This release of the MATTR VII Management API (v1.29.1) is a maintenance release. It includes internal changes to enhance performance and stability.
## Analytics event purging configuration and Status List specification update in MATTR VII
2026-03-16
Tags: Platform Management, Issuance
This release of the MATTR VII platform (v12.11.0) introduces the following enhancements:
* **Configurable analytics event purging**: The schedule in which Platform Events that are older than the set retention period are purged can now be configured per tenant. [Contact us](mailto:dev-support@mattr.global) if you want to change this setting for your tenant.
* **Support for Draft 14 of the Status List specification**: MATTR VII now supports Draft 14 of the Status List specification for mDocs revocation. This update introduces several key changes to improve alignment with the latest standards:
| Aspect | Legacy Format | Latest Specification |
| -------------------------------- | -------------------------------------- | ---------------------------- |
| **MSO field name** | `_status` | `status` |
| **Token header `typ`** | `mattr-statuslist+cwt` | `application/statuslist+cwt` |
| **Status bits per credential** | 2 bits | 1 bit |
| **Available status values** | Valid, Invalid, Suspended (Deprecated) | Valid, Invalid |
| **CBOR payload claim numbers** | Negative keys (-65538, -65539) | Positive keys (65533, 65534) |
| **Distribution response format** | Array of objects with `uri` property | Array of URI strings |
| **Maximum credentials per list** | 500,000 | 500,000 |
Currently all MATTR VII tenants continue to use the legacy specification format by default. In the next release, we will enable the Draft 14 specification for all tenants by default. If you wish your tenant to keep using the legacy specification, please [contact us](mailto:dev-support@mattr.global) to ensure we do not enable the new specification for your tenant.
Refer to [mDocs revocation](/docs/issuance/revocation/overview) for more information.
## Analytics event tracking in MATTR VII Management API
2026-03-16
Tags: Platform Management
This release of the MATTR VII Management API (v1.29.0) introduces the following enhancement:
* **Analytics events for GET /v1/events**: Consistent with all other Management API endpoints, the analytics service now generates events when `GET /v1/events` is called.
## Managed IACA deletion and bug fixes in the MATTR Portal
2026-03-16
Tags: Maintenance, Platform Management, Issuance
This release of the MATTR Portal (v1.73.1) introduces the following enhancements and fixes:
* **Managed IACA deletion**: Managed IACAs can now be deleted by users with `admin` or `issuer` roles. This provides greater flexibility in managing your certificate authorities and helps maintain a clean and organized certificate inventory.
* **Binary claim file upload fix**: Fixed an issue where uploading a file to a Binary claim in Pre-authorized Code flow credential offers included a Data URL prefix (e.g., `data:image/jpeg;base64,`) before the actual base64 content. This prefix caused credential validation failures when adding credentials to certain wallets. The Portal now correctly handles file uploads to ensure only raw base64 content is used in the claim value.
* **Form field HTML tag fix**: Fixed an issue where it was possible to add ``, `
`, ``, and `` HTML tags into form fields, which resulted in them being rendered in some delete confirmation modals.
## Maintenance update in MATTR VII Management API
2026-03-02
Tags: Maintenance, Platform Management
This release of the MATTR VII Management API (v1.28.2) is a maintenance release. It includes internal changes to enhance performance and usability.
## Singapore region now available
2026-03-02
Tags: Platform Management
A new MATTR VII Singapore region is now available, joining the existing regions of New Zealand (Auckland), Australia (Sydney), Europe (Frankfurt), United States (Oregon), and Canada (Montreal). This supports implementations from Singapore that need their infrastructure and data hosted within Singapore boundaries.
Customers can now use the MATTR Portal to create new tenants in the new SG region.
## SSO enhancements, JWT access tokens, and analytics improvements in MATTR VII
2026-03-02
Tags: Platform Management, Issuance, Verification
This release of the MATTR VII platform (v12.10.0) introduces the following enhancements and fixes:
* **Improved SSO user experience**: SSO connection users are no longer prompted to complete MFA enrolment or verification, and are no longer required to verify their email address. This streamlines the sign-in flow for users authenticating via an SSO provider.
* **JWT access token from the Token endpoint**: The `POST /v1/oauth/token` endpoint now returns a JWT access token instead of the previously used opaque token. This applies to both the Pre-authorized Code and Authorization Code flows, giving clients richer, self-contained token data.
* **Analytics events for GET /v1/events**: Consistent with all other tenant endpoints, the analytics service now generates events when `GET /v1/events` is called.
* **Empty credential issuance validation (bug fix)**: In alignment with OID4VCI, the platform now correctly rejects any attempt to issue a credential that contains no claims. Requests of this kind will fail with an appropriate error response.
## Email handling fix in MATTR VII Management API
2026-02-25
Tags: Platform Management, Issuance
This release of the MATTR VII Management API (v1.28.1) includes the following fix:
* **Email case handling fix (SSO users)**: Fixed an issue where some SSO users with mixed-case email addresses could encounter errors when accepting tenant invites. Email addresses for SSO users are now handled consistently, preventing errors regardless of casing.
## Managed Issuer certificate visibility and accessibility improvements in the MATTR Portal
2026-02-18
Tags: Platform Management, Issuance
This release of the MATTR Portal (v1.72.0) introduces the following enhancements:
* **Managed Issuer visibility of child certificates**: Managed Issuers can now only view pending and active Document Signer Certificates (DSC) and Status List Signer Certificates (SLSC). They cannot create, edit, or delete DSCs/SLSCs.
* **Updated custom domain asset requirements**: Updated the notes on the Custom Domain page to reflect the changes required for the issuer authorization server metadata endpoint.
* **Accessibility improvements**: Fixed accessibility issues to improve usability and compliance.
## Client Attestation enhancements in MATTR VII
2026-02-16
Tags: Issuance, Holding
This release of the MATTR VII platform (v12.9.0) introduces the following enhancements:
* **Client Attestation 'Standard Mode' support**: We've enhanced our Client Attestation capability to support an alternative proof-of-possession method called 'Standard Mode', which sits alongside our existing 'Combined Mode' option. **Standard Mode** is designed for wallets that provide a separate Attestation PoP alongside the Attestation JWT (with or without DPoP), rather than combining the attestation proof with the DPoP proof. This gives wallet developers more flexibility in how they implement client attestation, by offering an option for implementations who prefer not to share the same key for both DPoP and attestation proof.
* **Credential Instance ID persistence**: If a holder app chooses to pass a Credential Instance ID when performing Client Attestation with a MATTR VII issuer, we now persist and return this data against the issued credential in the credential registry. This allows issuers to have better traceability of which specific app instance a credential was issued to, and use this information for later analysis or troubleshooting.
* **Issuer metadata fix**: Resolved an issue where issuer metadata was not being properly exposed for tenants with a configured custom domain.
Client Attestation is currently being offered as a closed beta preview. If you would like to participate, please [contact us](mailto:dev-support@mattr.global).
## Tenant creation restriction in MATTR VII Management API
2026-02-16
Tags: Platform Management
This release of the MATTR VII Management API (v1.28.0) introduces the following change:
* **Tenant creation restriction**: Users who are invited to existing tenants via the MATTR Portal can no longer create new tenants themselves. This improves governance and ensures tighter control over tenant management.
## Improved tenant governance and maintenance updates in the MATTR Portal
2026-02-11
Tags: Maintenance, Platform Management
This release of the MATTR Portal (v1.71.0) introduces the following enhancements:
* **Enhanced tenant governance**: Users who are invited to existing tenants via the MATTR Portal can no longer create new tenants of their own, improving governance and control over tenant management.
* **Terminology updates**: Renaming of credential formats and related terms across the Portal, along with updates to all credential configuration forms to include consistent messaging and helpful links.
* **Improved error reporting**: Sentry has been enabled as an error reporting tool, enhancing data logging and error resolution capabilities.
## Webhook Payload Consistency Fix in MATTR VII
2026-02-04
Tags: Platform Management, Issuance
This release of the MATTR VII platform (v12.8.1) resolves an issue where the `OpenIdCredentialIssued` webhook event payload included an unexpected change. The [payload structure](/docs/platform-management/webhooks-guide#event-payload) is now consistent with previous releases.
## New Zealand region now available
2026-02-02
Tags: Platform Management
A new MATTR VII New Zealand region is now available, joining the existing regions of Australia (Sydney), Europe (Frankfurt), United States (Oregon), and Canada (Montreal). This supports implementations from New Zealand that need their infrastructure and data hosted within New Zealand boundaries.
Customers can now use the MATTR Portal to create new tenants in the new NZ region.
## OID4VCI v1.0 alignment and client attestation closed beta preview in MATTR VII
2026-02-02
Tags: Issuance
This release of the MATTR VII platform (v12.8.0) introduces the following enhancements:
### OID4VCI v1.0 alignment [#oid4vci-v10-alignment]
MATTR VII now aligns with OpenID for Verifiable Credential Issuance (OID4VCI) v1.0. This enhances interoperability and ensures all relevant parties are following the same specification. The following changes have been implemented:
1. **Separate OAuth server metadata endpoint**: OAuth server metadata, previously exposed via `GET {tenant_url}/.well-known/openid-credential-issuer`, are now available through a dedicated endpoint. See the [authorization server metadata endpoint](/docs/api-reference/platform/issuer-metadata/wellKnownOauthAuthorizationServer) in the API Reference for more information.
2. **Supported credential metadata location change**: Supported credential metadata are now exposed under the `credential_configurations_supported` field (instead of `credentials_supported`) in the response from `GET {tenant_url}/.well-known/openid-credential-issuer`. The new structure contains similar information with an updated format. See the [issuer metadata endpoint](/docs/api-reference/platform/issuer-metadata/wellKnownOidcConfig) in the API Reference for more information.
3. **Credential offer payload change**: In a credential offer payload, the `credentials` field has been replaced with `credential_configuration_ids`. This field includes a list of credential configuration IDs corresponding to the credentials offered to a wallet client. Wallet clients should use these IDs for looking up credential metadata exposed from the credential issuer metadata endpoint. See the [credential offer endpoint](/docs/issuance/credential-offer/api-reference) in the API Reference for more information.
4. **Credential request structure changes**:
* `credential_configuration_id` is a new parameter that specifies which credential to issue. This corresponds to the credential configuration ID from the credential offer and the issuer metadata. Previously, this information was included in the `format`, `credential_definition`, `types`, and `doctype` fields (depending on credential format).
* `proofs` object provides proof of possession of the cryptographic key material to which the issued credential instances will be bound. This was previously passed in the `proof` parameter (singular).
* While the OID4VCI v1.0 structure supports an array of proofs for batch issuances, MATTR VII currently supports a single proof only and will enable batch issuance in the future.
* No change is required in the structure of the previously provided `proof` payload.
See the [credential issuance endpoint](/docs/api-reference/platform/credential-issuance/postOpenIdCredential) in the API Reference for more information.
5. **Credential response structure change**: The response now contains a `credentials` array of issued credentials (instead of the previous `credential` and `format` elements).
* While the OID4VCI v1.0 structure supports an array of credentials for batch issuances, MATTR VII currently supports single credential issuance only and will enable batch issuance in the future.
See the [credential issuance endpoint](/docs/api-reference/platform/credential-issuance/postOpenIdCredential) in the API Reference for more information.
**Backwards compatibility and deprecation timeline**
1. This change is backwards compatible. Wallet applications making a request using the old structure will receive the old response structures as well.
2. As per our SLA, the previous fields in the API Reference have been marked as deprecated and will reach their End of Life (EOL on May 2nd, 2026), when they will be removed from the platform. You are advised to update your implementation to support this new structure. If you have any concerns about this timeline or need assistance with migration, please [contact us](mailto:dev-support@mattr.global).
### Client attestation closed beta preview [#client-attestation-closed-beta-preview]
MATTR VII now offers client attestation as a closed beta preview. Participating customers can:
* Configure their tenants so that they will only issue credentials into a wallet that provides DPoP-based client attestation (by adding a client attestation root certificate against a trust wallet list configuration for that tenant).
* Pass a client instance ID as part of the client attestation. This identifier will be included in the access token returned to the wallet client.
* Use this passed client instance ID as part of a request sent to a configured [claims source](/docs/issuance/claims-source/overview#request-parameters).
Refer to [client attestation](/docs/issuance/credential-issuance/wallet-attestation) for more information.
If you would like to participate in the closed beta preview, please [contact us](mailto:dev-support@mattr.global).
## SSO authentication and role-based user invitations in MATTR VII
2026-01-27
Tags: Platform Management, Issuance
This release of the MATTR VII Management APIs (1.27.0) introduces the following enhancements:
* **SSO authentication for Portal access**: The Management API now enables customers to use their Entra ID/Azure AD identity provider (IdP) to authenticate users accessing the MATTR Portal through single sign-on (SSO). This enables organisations to manage Portal access through their existing identity infrastructure, streamlining user authentication and improving security.
* **User invitations for managed-issuer role**: Users with the `managed-issuer` role can now invite other users with the same role to the Portal. This enables delegated user management within the same permission scope.
If you would like to use your supported SSO provider to access the MATTR Portal in your organisation, please [contact us](mailto:dev-support@mattr.global).
## SSO support and Android verifier configuration now available in the MATTR Portal
2026-01-27
Tags: Platform Management, Verification
This release of the MATTR Portal (v1.70.0) introduces the following enhancements:
* **SSO support**: Customers can now use their Entra ID/Azure AD identity provider (IdP) to authenticate users accessing the MATTR Portal through single sign-on (SSO). This enables organisations to manage Portal access through their existing identity infrastructure, streamlining user authentication and improving security.
* **Android verifier configuration**: You can now use the Portal to manage [Android Verifier Applications](/docs/verification/remote-mobile-verifiers/journey-pattern), enabling you to configure and manage Android mobile app verification workflows.
* **New Managed Issuer role**: The Portal now supports the new [`managed-issuer`](/docs/platform-management/roles-and-permissions#managed-issuer) role. This role is similar to the `issuer` role, except it cannot create new IACAs and cannot manage credential configurations. This supports different operational models where issuers are managed by a higher level of admins or issuers who control most of the settings.
If you would like to enable SSO for your organisation to access the Portal,
please [contact us](mailto:dev-support@mattr.global).
## E2E Encryption spec alignment
2025-12-18
Tags: Issuance
This release of the MATTR VII platform (v12.7.2) aligns the End-to-End Encryption (E2E Encryption) credential request format with the OID4VCI specification.
## Bug fix for E2E Encryption in MATTR VII
2025-12-17
Tags: Maintenance, Issuance
This release of the MATTR VII platform (v12.7.1) fixes an issue where the JWKS format for [E2E encryption](/docs/issuance/credential-issuance/e2e-encryption) was incorrect in both the credential issuer metadata and credential request format. This fix ensures proper encryption key exchange between issuers and wallets.
## Enhanced security and operational flexibility in MATTR VII
2025-12-15
Tags: Issuance, Holding
This release of the MATTR VII platform (v12.7.0) introduces the following enhancements:
* **New Managed Issuer role**: The new `managed-issuer` role is similar to the `issuer` role, except it cannot create new IACAs and cannot manage credential configurations. This supports different operational models where issuers are managed by a higher level of admins or issuers who control most of the settings.
* **End-to-end encryption for credential issuance**: End-to-End Encryption (E2E Encryption) provides an additional layer of security for credential issuance workflows where intermediary services are involved between the credential issuer and the wallet application. E2E Encryption ensures that credential data and personally identifiable information (PII) remain confidential and unreadable to intermediary backend servers, even as they facilitate the credential delivery process. The credentials are encrypted end-to-end, from the issuer directly to the specific wallet instance, ensuring that only the intended recipient can decrypt and access the credential data. End-to-End Encryption is currently in technical preview and must be enabled on a per-tenant basis. If you would like to enable this feature for your tenant, please [contact us](mailto:dev-support@mattr.global).
* **IP address in platform analytics events**: Platform analytics events now include the IP address the request came from.
## MATTR VII Management API maintenance release
2025-12-15
Tags: Maintenance, Platform Management
This release of the MATTR VII Management APIs (v1.26.1) is a maintenance release. It includes
incremental internal updates to support upcoming features.
## Credential Refresh beta preview in MATTR VII
2025-11-24
Tags: Issuance
This release of the MATTR VII platform (v12.6.0) introduces Credential Refresh as a beta preview feature.
[Credential Refresh](/docs/issuance/refresh/overview) enables wallets to claim new instances of credentials without requiring the user to authenticate again. This capability supports several use cases, such as validity extension with a streamlined user experience.
Credential Refresh is offered as a closed beta feature and is not generally available yet. Functionality may be limited, may not work in all scenarios, and could change or break without prior notice. If you are interested in participating in the closed beta, please [contact us](mailto:dev-support@mattr.global).
## MATTR VII Management API maintenance release
2025-11-24
Tags: Maintenance, Platform Management
This release of the MATTR VII Management APIs (v1.26.0) is a maintenance release. It includes
internal changes to enhance performance and usability.
## Bug fixes and maintenance improvements in the MATTR Portal
2025-11-13
Tags: Maintenance, Platform Management
This release of the MATTR Portal (v1.69.0) introduces the following enhancements:
* **Participant points of contact**: Added hint text to recommend a minimum of two points of contact per participant.
This release also includes bug fixes and maintenance enhancements to improve overall performance and stability.
## MATTR VII Management API maintenance release
2025-11-10
Tags: Maintenance, Platform Management
This release of the MATTR VII Management APIs (v1.25.0) is a maintenance release. It includes
internal changes to enhance performance and usability.
## Streamlined configuration of participant document types in MATTR VII
2025-11-10
Tags: Trust Networks
This release of the MATTR VII platform (v12.5.0) streamlines configuration of document types ecosystems participants are permitted to issue.
When you create or update a participant, you can now define which document types participants are permitted to issue at the IACA level. This enhancement extends the IACA docTypes feature across ecosystem policy and participant specifications, providing more granular control over participant permissions within your ecosystem.
## Revocable mDocs support, enhanced ecosystem management and security improvements in the MATTR Portal
2025-11-06
Tags: Platform Management, Issuance, Trust Networks
This release of the MATTR Portal (v1.68.0) introduces the following enhancements:
* **Revocable mDocs**: Create credential configurations that let you later change mDoc revocation status (valid, invalid, suspended) for stronger lifecycle control.
* **Points of contact**: Manage up to 10 contact records per ecosystem participant.
* **Participant evidence PDFs**: Upload, view, replace, and delete PDF evidence linked to an ecosystem participant.
* **Simplified ecosystem configuration**: Configure document types directly when uploading an IACA certificate to a participant, simplifying the ecosystem configuration process. This change also removes the ecosystem credential types and participant roles tabs from the Portal.
* **Inactivity timeout**: Users are automatically signed out after a period of inactivity to reduce risk from unattended sessions.
This release also includes bug fixes and maintenance enhancements to improve overall performance and stability.
## Adding Points of Contact and PDF Evidence to Issuer Participants
2025-10-29
Tags: Issuance, Trust Networks
This release of the MATTR VII platform (v12.4.0) introduces the following enhancements to participants records:
* **Adding points of contact**: DTS operators can now manage up to 10 records of points of contact for each participant.
* **Adding PDF evidence**: DTS operators can now upload and manage PDF files associated with specific participants.
## MATTR VII Management API maintenance release
2025-10-29
Tags: Maintenance, Platform Management
This release of the MATTR VII Management APIs (v1.24.0) is a maintenance release. It includes
internal changes to enhance performance and usability.
## Enhanced event log filtering in the MATTR Portal
2025-10-23
Tags: Platform Management
This release of the MATTR Portal (v1.67.1) introduces enhanced event log capabilities.
The Portal now displays the requestor (user ID, client ID, or system) for each event in the event logs, providing better visibility into who
initiated each action. You can now filter event logs by requestor to quickly find events
associated with specific users, clients, or system processes, making audit trails more transparent
and actionable.
This release also includes bug fixes and maintenance enhancements to improve overall performance and
stability.
## DSC and SLSC revocation via CRL now available in MATTR VII
2025-10-13
Tags: Issuance
This release of the MATTR VII platform (v12.3.0) introduces the following enhancements:
* **DSC and SLSC revocation via CRL**: Issuers using MATTR-managed IACAs can now revoke their
\[Document Signer Certificates] (DSCs) and [Status List Signer Certificates](/docs/issuance/revocation/api-reference/mdocs-status-list-signers#revoke-a-status-list-signer) (SLSCs) via Certificate
Revocation List (CRL). This provides greater control over certificate lifecycle management and
enhances security by enabling timely revocation of compromised or outdated certificates.
* **Updated contact details for DTS participants**: The contact details fields for Digital Trust
Service (DTS) participants have been updated to clearly indicate they represent high-level
organisation contact details. This change prepares for upcoming features that will allow listing
individual points of contact separately, providing more granular contact management capabilities.
## MATTR VII Management API maintenance release
2025-10-13
Tags: Maintenance, Platform Management
This release of the MATTR VII Management APIs (v1.23.1) is a maintenance release. It includes
internal changes to enhance performance and usability.
## Pre-authorized credential offers and DTS participant field updates in the MATTR Portal
2025-10-13
Tags: Platform Management, Trust Networks
This release of the MATTR Portal (v1.66.0) introduces the following enhancements:
* **Pre-authorized credential offers**: The Portal now supports creating and testing [pre-authorized
credential offers](/docs/issuance/pre-authorized-code/overview), enabling users to configure and validate this issuance flow directly within the
Portal. Refer to our [tutorial](/docs/issuance/pre-authorized-code/tutorial) for a hands-on example.
* **Updated field names for Ecosystem participants**: Changes have been implemented to support the
new field names for Digital Trust Service (DTS) participants introduced in the [latest platform
release](#dsc-and-slsc-revocation-via-crl-now-available-in-mattr-vii), ensuring consistency across the platform and Portal experiences.
Creating pre-authorized code flow credential offers in the MATTR Portal is for
testing purposes only. The populated user ID is generated by the MATTR Portal
and cannot be changed to another user later.
Sign up for a trial [here](/docs/resources/get-started) and start exploring
the MATTR Portal today!
## Enhanced malware scanning for digital pass templates
2025-09-29
Tags: Issuance
This release of the MATTR VII platform (v12.2.0) extends our malware scanning capabilities to
include zip folders uploaded for digital pass templates.
When creating templates for [Apple](/docs/issuance/cwt-credential-templates/apple-templates)
and [Google](/docs/issuance/cwt-credential-templates/google-templates) digital passes, any zip folders containing assets
(such as images, icons, or other resources) are now automatically scanned for malware before
processing. This enhancement strengthens security across the digital pass creation workflow while
maintaining the same user experience.
## MATTR VII Management API maintenance release
2025-09-29
Tags: Maintenance, Platform Management
This release of the MATTR VII Management APIs (v1.23.0) is a maintenance release. It includes
internal changes to enhance performance and usability.
## MATTR VII Management API maintenance release
2025-09-15
Tags: Maintenance, Platform Management
This release of the MATTR VII Management APIs (v1.22.1) is a maintenance release. It only includes dependency upgrades.
## MATTR VII Platform maintenance release
2025-09-15
Tags: Maintenance
This release of the MATTR VII platform (v12.1.0) is a maintenance release. It introduces internal
changes to support future features and improvements.
## Expanded certificate management and automated VICAL scheduling now available in the MATTR Portal
2025-09-01
Tags: Platform Management, Issuance, Trust Networks
This release of the MATTR Portal (v1.65.0) introduces enhanced certificate management features and
greater automation for credential issuance:
* You can now manage a wider range of certificate authorities directly in the Portal, including both
externally managed and MATTR-managed:
* IACAs
* DTS root CA
* Verifier root CA
* VICAL Configuration now allows you to automatically generate VICALs on a **daily** or **weekly**
basis.
* Date display enhancements have been introduced: hovering over a date now reveals a popup showing
both **local time** and **relative time**, improving clarity for users across time zones.
Sign up for a trial [here](/docs/resources/get-started) and start exploring
the MATTR Portal today!
## Management API Domain Migration
2025-09-01
Tags: Maintenance, Platform Management
This release of the MATTR VII Management APIs (v1.22.0) is a maintenance release. It introduces
backend changes required to support new features in the MATTR VII platform.
In addition, and in line with our SLA, we are providing an **end-of-life (EOL) notification** for
the legacy Management API domain:
* The previous domain `https://manage.mattr.global` is now officially **deprecated**.
* Customers must complete migration to the new domain `https://manage.au01.mattr.global` before **1
March 2026**.
* After this date, the legacy domain will no longer be supported.
* This change only impacts customers using **machine clients** to access the Management API.
Please update your integrations to ensure continuity of service before the deprecation deadline.
## Support for externally managed Verifier and DTS root CA certificates now available in MATTR VII
2025-09-01
Tags: Issuance, Verification, Trust Networks
This release of the MATTR VII platform (v12.0.0) introduces new capabilities for customers managing
their own trust infrastructure, providing greater flexibility in establishing trust across your
network.
You can now integrate with MATTR VII using externally managed certificates from your existing Public
Key Infrastructure (PKI):
* [Verifier Root CA certificates](/docs/verification/certificates/overview): MATTR VII can now use
these to sign **Verification Request Signer Certificates (VRSCs)**, which are then used to sign
verification requests.
* [DTS Root CA certificates](/docs/digital-trust-service/certificates-overview): MATTR VII can now use
these to sign **VICAL Signer Certificates (VSCs)**, which are then used to sign VICALs.
These updates enable closer alignment with your existing PKI while still benefiting from MATTR VII’s
Digital Trust Service (DTS) and Credential Verification capabilities.
## MATTR VII Management maintenance release
2025-08-18
Tags: Maintenance, Platform Management
This release of the MATTR VII management APIs (v1.21.0) is a maintenance release. It introduces
backend changes required to support new features in the MATTR VII platform.
## Web verifier applications now support dynamic URIs
2025-08-18
Tags: Verification
This release of the MATTR VII platform (v11.3.0) introduces support for wildcard path fragments in
redirect URIs within verifier applications.
Verifier apps can now whitelist
[redirect URIs](/docs/verification/remote-verification-api-reference/verifier-applications#create-a-verifier-application!path=0/openid4vpConfiguration/redirectUris\&t=request)
that include an asterisk in the path component (e.g., `https://www.example.com/*/callback`),
enabling support for dynamic callback paths such as those containing session IDs.
## MATTR Portal maintenance release
2025-08-14
Tags: Maintenance, Platform Management
This release of the MATTR Portal (v1.64.0) is a maintenance release. It introduces changes required
to support new backend features that are coming up.
Sign-up for a trial [here](/docs/resources/get-started) and start exploring
the MATTR Portal today!
## Enhanced client analytics tracing in MATTR VII
2025-08-04
Tags: Platform Management, Issuance, Trust Networks
This release of the MATTR VII platform (v11.2.0) introduces the following enhancements:
* Client activity tracing: You can now filter analytic events by client ID,
making it easier to trace specific client actions.
* VICAL configuration timestamp: We’ve added visibility into when your VICAL configuration was
last set, helping you predict when the next version will be published based on your auto-publish schedule.
* New `events` endpoint: A new
[`/v1/events`](/docs/platform-management/analytics/api-reference#retrieve-analytic-events) endpoint is available as
an alternative to /analytics. This change helps avoid issues with browsers that block requests
containing the term "analytics". In alignment with our
[Service Level Agreement (SLA)](/docs/resources/terms/service-level-agreement), the existing
`/v1/analytics/events` endpoint has been marked as *deprecated*/*retired* and will reach its EOL on February 4th 2026,
when it will be removed from the MATTR VII platform.
* IACA activation error handling: If you attempt to activate an
[unmanaged IACA](/docs/issuance/certificates/guide) that does not have any Document Signer
Certificates signed by it, you will now receive a clear error message. To resolve this, activate a
Document Signer Certificate signed by the unmanaged IACA before activating it.
## MATTR VII Management maintenance release
2025-08-04
Tags: Maintenance, Platform Management
This release of the MATTR VII management APIs (v1.20.0) is a maintenance release. It introduces
backend changes required to support new features in the MATTR VII platform.
## MATTR Portal maintenance release
2025-07-24
Tags: Maintenance, Platform Management
This release of the MATTR Portal (v1.63.0) is a maintenance release. It introduces backend changes
required to support new features that are coming up.
Sign-up for a trial [here](/docs/resources/get-started) and start exploring
the MATTR Portal today!
## MATTR VII Management maintenance release
2025-07-21
Tags: Maintenance, Platform Management
This release of the MATTR VII management APIs (v1.19.0) is a maintenance release. It introduces
backend changes required to support exciting new features that are coming up.
## VICAL auto-publishing now available in MATTR VII
2025-07-21
Tags: Trust Networks
This release of the MATTR VII platform (v11.1.0) introduces the ability to automatically publish
your [VICAL](/docs/digital-trust-service/vical-overview) according to a pre-set schedule. The
[VICAL configuration](/docs/digital-trust-service/vical-api-reference/configuration)
can now be adjusted so that the VICAL is automatically published daily or weekly, ensuring that it
is always up-to-date with the latest information without requiring manual intervention.
## HSM support, external Issuer certificates and Verify with Wallet API now available in MATTR VII
2025-07-08
Tags: Issuance, Verification, Holding
This release of the MATTR VII platform (v11.0.0) introduces several new features and enhancements:
* Hardware Security Module (HSM) support for key management: We’ve introduced support for customers
to use a MATTR-managed **Hardware Security Module (HSM)** for key management. This provides an
additional layer of assurance for protecting cryptographic keys, beyond our standard **Software
Security Module (SSM)**.
* **Note:** HSM usage incurs additional costs and requires a commercial agreement. Please reach
out to your account team if you’re interested in enabling this feature.
* External IACA Support: You now have the flexibility to manage your own **Issuer Authority
Certificate Authority (IACA)**, **Document Signing Certificates (DSCs)** and **Status List Signer
Certificates (SLSCs)**. This offers flexibility for organizations with specific Public Key
Infrastructure (PKI) requirements. It preserves the integrity of the mDoc trust chain while giving
issuers full control over their cryptographic materials. Refer to
[external issuer certificates](/docs/concepts/chain-of-trust#external-certificates) for more information.
* Support for Verify with Apple Wallet API: We’ve added the necessary configuration to support the
[Verify with Wallet API](https://developer.apple.com/wallet/get-started-with-verify-with-wallet/)
in iOS mobile apps, including a required **Apple Team ID**. This sets the stage for upcoming
enhancements in our [iOS Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview) that
will enable streamlined credential verification flows in mobile applications.
* Enhanced Credential Metadata in API Responses: We’ve enriched the data returned when
[retrieving users' credentials metadata](/docs/api-reference/platform/users/getUserCredentials)
with new fields such as *Issuance Date*, *Valid From*, and *Valid Until*. These additions provide
greater visibility into credential lifecycles.
## Support for external Issuer certificates and Verify with Wallet API now available in MATTR Portal
2025-07-08
Tags: Platform Management, Issuance, Verification, Holding
This release of the MATTR Portal (v1.62.2) introduces changes to support new capabilities offered by
the MATTR VII platform:
* To comply with changes made to support the Verify with Apple Wallet API, when creating an iOS
Verifier Application the following changes are now in effect:
* The Team ID property is required.
* The OID4VP configuration is optional.
* To comply with changes made to enable external issuer certificates, IACAs are always created as
inactive by default, and you must manually set them to active after they are created.
Sign-up for a trial [here](/docs/resources/get-started) and start exploring
the MATTR Portal today!
## MATTR VII Management maintenance release
2025-07-07
Tags: Maintenance, Platform Management
This release of the MATTR VII management APIs (v1.18.0) is a maintenance release. It introduces
backend changes required to support exciting new features that are coming up.
## MATTR VII Management maintenance release
2025-06-24
Tags: Maintenance, Platform Management
This release of the MATTR VII management APIs (v1.17.0) is a maintenance release. It introduces
backend changes required to support exciting new features that are coming up.
## Use MATTR VII to configure explicit validity periods for issued mDocs
2025-06-24
Tags: Issuance
This release of the MATTR VII platform (v10.4.0) introduces the ability to
[create mDocs credential configurations](/docs/issuance/credential-configuration/api-reference/mdocs#create-an-mdocs-credential-configuration)
that define explicit:
* *Activation date* using the
[`validFrom`](/docs/issuance/credential-configuration/api-reference/mdocs#create-an-mdocs-credential-configuration!path=validFrom\&t=request)
parameter. This allows issuers to create mDocs that are valid from an explicit future date,
enabling scenarios where credentials can be issued in advance and activated later.
* *Expiration date* using the
[`validUntil`](/docs/issuance/credential-configuration/api-reference/mdocs#create-an-mdocs-credential-configuration!path=validUntil\&t=request)
parameter. This allows issuers to set a specific end date for the validity of mDocs credentials,
ensuring that credentials are only valid for a defined period.
## UX enhancements and bug fixes in the MATTR Portal
2025-06-17
Tags: Maintenance, Platform Management
This release of the MATTR Portal (v1.61.1) allows you to paste string data directly into form fields
that accept PEM files as identifiers. This update also includes various bug fixes and enhancements
to improve the overall user experience.
Sign-up for a trial [here](/docs/resources/get-started) and start exploring
the MATTR Portal today!
## Enhanced OID4VCI flows traceability in MATTR VII
2025-06-09
Tags: Issuance
This release of the MATTR VII platform (v10.3.0) introduces enhanced traceability for
[OID4VCI flows](/docs/issuance/oid4vci-overview). This includes adding parameters to relevant analytics
events, enabling better tracking and analysis of platform events.
The following analytic events have been updated to include the `sessionId` parameter:
* Create a Pre-authorized Code offer:
* `OPENID_PRE_AUTHORIZED_OFFER_CREATE_START`
* `OPENID_PRE_AUTHORIZED_OFFER_CREATE_SUCCESS`
* Delete a Pre-authorized Code offer:
* `OPENID_PRE_AUTHORIZED_OFFER_DELETE_START`
* `OPENID_PRE_AUTHORIZED_OFFER_DELETE_SUCCESS`
* If the provided `offerId` does not exist, the `OPENID_PRE_AUTHORIZED_OFFER_DELETE_FAIL` will
include the `offerId`.
* Authorization Code flow:
* `OPENID_AUTHORIZE_SUCCESS`
* `OPENID_AUTHENTICATION_PROVIDER_AUTHORIZE_SUCCESS`
* `OPENID_AUTHENTICATION_PROVIDER_AUTHORIZE_START`
* If the user fails to authenticate with the configured authentication provider, the
`OPENID_AUTHENTICATION_PROVIDER_AUTHORIZE_FAIL` event will also include the `sessionId`.
* If the tenant fails to authenticate with a configured claim source, the
`OPENID_CLAIM_AUTHORIZATION_FAIL` event will include the `sessionId`.
Refer to the [MATTR VII Events
registry](https://api-reference-sdk.mattr.global/event-registry/latest/index.html)
for a complete list of MATTR VII Platform API events and their parameters.
## MATTR VII Management maintenance release
2025-06-09
Tags: Maintenance, Platform Management
This release of the MATTR VII management APIs (v1.16.0) is a maintenance release. It introduces
backend changes required to support exciting new features that are coming up.
## Mobile app verification now available in the MATTR Portal
2025-05-27
Tags: Platform Management, Verification
This release of the MATTR Portal (v1.60.0) enables you to configure and manage
[mobile app verification](/docs/verification/remote-mobile-verifiers/workflow) workflows in the
MATTR Portal. This includes:
* What mobile applications can interact with the MATTR VII tenant.
* What wallet applications can present credentials.
* What issuers can be trusted.
Sign-up for a trial [here](/docs/resources/get-started) and start exploring
the MATTR Portal today!
## MATTR VII Management maintenance release
2025-05-26
Tags: Maintenance, Platform Management
This release of the MATTR VII management APIs (v1.15.0) is a maintenance release. It introduces
backend changes required to support exciting new features that are coming up.
## New issuance and verification channels supported in MATTR VII
2025-05-26
Tags: Issuance, Verification
This release of the MATTR VII platform (v10.2.0) introduces new issuance and verification channels:
* MATTR VII now supports issuing credentials using the
[OID4VCI Pre-authorized Code flow](/docs/issuance/pre-authorized-code/overview). This flow
enables issuers to authenticate and authorize holders in advance, making credential issuance
faster and more streamlined for users who have already been verified. To support this, you can now
manually [create new users](/docs/api-reference/platform/users/createUser) and generate
[credential offers](/docs/issuance/credential-offer/overview) for them.
* By integrating MATTR VII with the MATTR Pi
[iOS mDocs Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview), you can build iOS
apps that remotely request and verify mDocs presented from another app on the same device. This
supports workflows defined by [ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html).
## MATTR Portal now available for trial users
2025-05-13
Tags: Platform Management
This release of the MATTR Portal (v1.59.0) makes the portal available to trial users. Trial users
can now access the Portal and explore the features of the MATTR VII platform through an intuitive
graphical user interface.
Sign-up for a trial [here](/docs/resources/get-started) and start exploring the MATTR Portal
today!
## Enhanced support for trial users in the Management API
2025-05-12
Tags: Platform Management
This release of the MATTR VII Management API (v1.14.0) introduces enhanced support for trial users.
This will enable users to more easily [trial](/docs/resources/get-started) MATTR VII and the
[MATTR Portal](/docs/platform-management/portal).
## New user information available in MATTR VII
2025-05-12
Tags: Issuance
This release of the MATTR VII platform (v10.1.0) enhances the
[user retrieval](/docs/api-reference/platform/users/getUsers) operations to include linked
account data in the response. This allows you to view and manage identity connections more easily
through a single API call, enabling advanced user interactions.
Additionally, requests to external [claim sources](/docs/issuance/claims-source/overview) have been
improved for greater reliability. Outbound requests now explicitly use IPv4, preventing resolution
failures that previously occurred when some services defaulted to unsupported IPv6.
## MATTR Portal user interface enhancements
2025-05-06
Tags: Platform Management
This release of the MATTR Portal (v1.58.0) introduces user interface enhancements to improve the
overall user experience. This includes refinements to the layout, navigation, and styling of various
components within the Portal.
Additionally, this release includes backend changes to support exciting new features that are coming
up.
The Portal is available to selected cloud environments. [Contact
us](https://mattr.global/contact) if you're interested in accessing these
features or learning more.
## Role based access control now available in the MATTR Portal
2025-04-16
Tags: Platform Management
This release of the MATTR Portal (v1.57.1) introduces Role Based Access Control (RBAC) capabilities.
This enables admins to manage permissions and access within specific MATTR VII tenants, to ensure
users can only access the functionalities they need to perform their role. This feature enables you
to use the Portal to manage [users](/docs/platform-management/portal#inviting-users) and [clients](/docs/platform-management/create-client) that can access your tenants.
The Portal UI is aligned with the user's role and the permissions assigned to them. This means that
users will only see the features and functionalities that are relevant to their role. This is
particularly useful for organizations with multiple users who have different responsibilities and
permissions.
Refer to [Access control](/docs/platform-management/access-control) for more information on
available roles and associated permissions.
The Portal is available to selected cloud environments. [Contact
us](https://mattr.global/contact) if you're interested in accessing these
features or learning more.
## MATTR VII Management maintenance release
2025-04-14
Tags: Maintenance, Platform Management
This release of the MATTR VII management APIs (v1.13.0) is a maintenance release. It introduces
backend changes required to support exciting new features that are coming up.
## Refined certificate capabilities in MATTR VII
2025-04-14
Tags: Issuance
This release of the MATTR VII platform (v10.0.0) introduces the following changes to certificate
management in MATTR VII:
* Added safeguards to prevent the use of non-compliant DSCs (originally created for signing non-mDL
credentials) for mDL signing, particularly when their validity period exceeds the specification
limit. Validation has also been added to the credential configurations endpoint to ensure early
failure for such cases (attempting to sign credentials with validity periods longer than 427 days
for mDLs or 3620 days for any other mDoc). Refer to
[certificate selection](/docs/issuance/certificates/overview#certificate-selection) for more
information.
* Updated IACA validation logic to allow the use of
[user-assigned country codes](https://www.iso.org/glossary-for-iso-3166.html). You can now
successfully create and validate IACAs with these codes.
## MATTR VII Management maintenance release
2025-03-31
Tags: Maintenance, Platform Management
This release of the MATTR VII management APIs (v1.12.0) is a maintenance release. It introduces
backend changes required to support exciting new features that are coming up.
## MATTR VII Platform maintenance release
2025-03-31
Tags: Maintenance
This release of the MATTR VII platform (v9.1.0) is a maintenance release. It introduces internal
enhancements and bug fixes to improve overall platform performance.
## User experience enhancements in the MATTR Portal
2025-03-27
Tags: Platform Management, Verification, Trust Networks
This release of the MATTR Portal (v1.56.0) introduces the following user experience enhancements:
* Refined fields names and descriptions in the Verifier application form for improved clarity.
* Renamed the **Ecosystem** navigation item to **Digital Trust Service** for better alignment with
service scope.
* Updated breadcrumb links to correctly direct users when navigating to pages with multiple tabs.
* Various bug fixes and minor enhancements.
The Portal is available to selected cloud environments. [Contact
us](https://mattr.global/contact) if you're interested in accessing these
features or learning more.
## MATTR VII Management maintenance release
2025-03-17
Tags: Maintenance, Platform Management
This release of the MATTR VII management APIs (v1.11.0) is a maintenance release. It introduces
backend changes required to support exciting new features that are coming up.
## Updated mDocs validity periods
2025-03-17
Tags: Issuance
This release of the MATTR VII platform (v9.0.0) introduces a change to the maximum validity of
issued mDocs to improve the efficiency of signer certificate management.
When issuing mDocs and setting `type` to `org.iso.18013.5.*.mDL` (issuing an mDL) the maximum
validity is now the shorter of 427 days or the remaining IACA validity period.
For any other `type` the maximum validity is now the shorter of 10 years minus 30 days or the
remaining IACA validity period.
## MATTR VII Management maintenance release
2025-03-03
Tags: Maintenance, Platform Management
This release of the MATTR VII management APIs (v1.10.0) is a maintenance release. It introduces
backend changes required to support exciting new features that are coming up.
## Role based access control available in MATTR VII
2025-03-03
Tags: Platform Management, Verification
This release of the MATTR VII platform (v8.0.0) introduces Role Based Access Control (RBAC)
capabilities. This enables admins to manage permissions and access within specific MATTR VII
tenants, to ensure users can only access the functionalities they need to perform their role. Refer
to [Access control](/docs/platform-management/access-control) for more information and to the
corresponding [tutorial](/docs/platform-management/access-control-tutorial) for a hands-on example.
Additionally, following our EOL announcement and in alignment with our
[Service Level Agreement (SLA)](/docs/resources/terms/service-level-agreement), we have removed support for
the BBS 2020 signature suite. This means that JSON credentials supporting
selective-disclosure can only be:
* Signed using the BBS 2022 signature suite.
* Verified if they were signed using the BBS 2022 signature suite.
## Deprecation of non-hyphenated endpoints
2025-02-26
Tags: Maintenance
Following our
[EOL announcement](#enhancements-and-improvements-across-different-mattr-vii-capabilities), and in
alignment with our [Service Level Agreement (SLA)](/docs/resources/terms/service-level-agreement), we have
removed support for several non-hyphenated endpoints, and replaced them with hyphenated endpoints
for better readability:
* `/v2/credentials/compact/digital-pass/apple` replaced `/v2/credentials/compact/digitalpass/apple`.
* `/v2/credentials/compact/digital-pass/apple/templates` replaced
`/v2/credentials/compact/digitalpass/apple/templates`.
* `/v2/credentials/compact/digital-pass/google` replaced
`/v2/credentials/compact/digitalpass/google`.
* `/v2/credentials/compact/digital-pass/google/templates` replaced
`/v2/credentials/compact/digitalpass/google/templates`.
* `/v2/credentials/compact-semantic/digital-pass/apple` replaced
`/v2/credentials/compact-semantic/digitalpass/apple`.
* `/v2/credentials/compact-semantic/digital-pass/apple/templates` replaced
`/v2/credentials/compact-semantic/digitalpass/apple/templates`.
* `/v2/credentials/compact-semantic/digital-pass/google` replaced
`/v2/credentials/compact-semantic/digitalpass/google`.
* `/v2/credentials/compact-semantic/digital-pass/google/templates` replaced
`/v2/credentials/compact-semantic/digitalpass/google/templates`.
* `/v1/users/authentication-providers` replaced `/v1/users/authenticationproviders`.
* `/v1/claim-sources` replaced `/v1/claim-sources`.
* `/v2/credentials/mobile/document-signers` replaced `/v2/credentials/mobile/document-signers`.
* `/v2/credentials/web-semantic/linked-data/convert` replaced
`/v2/credentials/web-semantic/linkeddata/convert`.
## Expanded support for online presentations signing protocols in MATTR VII
2025-02-17
Tags: Verification
This release of the MATTR VII platform (v7.1.0) expands the support for online presentations signing
protocols in MATTR VII. [mDocs online presentations](/docs/verification/remote-overview) can now be
signed by presenting devices using Device MAC Authentication. This is in addition to the existing
support for signing a presentation using Digital Signatures.
## MATTR VII Management maintenance release
2025-02-17
Tags: Maintenance, Platform Management
This release of the MATTR VII management APIs (v1.9.0) is a maintenance release. It introduces
backend changes required to support exciting new features that are coming up.
## User experience enhancements in the MATTR Portal
2025-02-17
Tags: Platform Management
This release of the MATTR Portal (v1.55.0) introduces the following user experience enhancements:
* The Custom domain configuration screen is now located in a dedicated page. It is accessible from
the main navigation panel.
* The Portal authentication domain is now `auth.manage.mattr.global`.
The Portal is available to selected cloud environments. [Contact
us](https://mattr.global/contact) if you're interested in accessing these
features or learning more.
## MATTR VII Management maintenance release
2025-02-03
Tags: Maintenance, Platform Management
This release of the MATTR VII management APIs (v1.8.0) is a maintenance release. It introduces
backend changes required to support exciting new features that are coming up.
## mDocs online verification configuration now supports future-dated IACAs
2025-02-03
Tags: Issuance, Verification
This release of the MATTR VII platform (v7.0.0) introduces support for future-dated IACAs as part of
mDoc online verification workflows configuration.
When setting up a new trusted issuer, you now have the option to add a PEM representing an IACA with
a future activation date. This allows issuers to distribute their IACAs' PEM certificates ahead of
time and relying parties to seamlessly switch to a new one when required, ensuring continuous
operation without downtime.
*Breaking changes*
We've made changes to the `identifiers.mobile` element of the request body structure in the
[create](/docs/api-reference/platform/participants/createEcosystemParticipant)
and
[update](/docs/api-reference/platform/participants/updateEcosystemParticipant)
participant endpoints. Refer to the provided links for the new structure.
## Online verification configuration now available in the Self Service Portal
2025-01-23
Tags: Platform Management, Verification
This release of the MATTR Portal (v1.53.0) introduces the capability to use the portal
to configure
[online mDocs verification workflows](/docs/verification/remote-web-verifiers/workflow) on your
MATTR VII tenants. This includes configuring and managing:
* What verifier applications can interact with the MATTR VII tenant.
* What wallet applications can present mDocs for online verification.
* What issuers can be trusted when verifying mDocs presented online.
The Self Service Portal is available to selected cloud environments. [Contact
us](https://mattr.global/contact) if you're interested in accessing these
features or learning more.
## mDocs Online presentation bug fix in MATTR VII
2025-01-21
Tags: Maintenance, Verification
This release of the MATTR VII platform (v6.0.1) fixes up a bug that prevented multiple verifier
applications from using different application configurations. This issue has been resolved, and each
application can now use a specific
[application configuration](/docs/verification/remote-verification-api-reference/verifier-applications#create-a-verifier-application).
## MATTR VII Management maintenance release
2025-01-08
Tags: Maintenance, Platform Management
This release of the MATTR VII management APIs (v1.7.0) is a maintenance release. It introduces
backend changes required to support exciting new features that are coming up.
## Multiple Verifier applications support in MATTR VII
2025-01-08
Tags: Verification
This release of the MATTR VII platform (v6.0.0) introduces support for multiple verifier
applications interacting with a single MATTR VII tenant to perform
[online verification](/docs/verification/remote-web-verifiers/workflow) of [mDocs](/docs/concepts/mdocs).
This means that a single MATTR VII tenant can now be configured to handle mDocs online verification
requests from different web applications, and handle these requests differently based on the
requesting application.
**Breaking changes**
* A
[new endpoint](/docs/verification/remote-verification-api-reference/verifier-applications#create-a-verifier-application)
is now available to configure a verifier application, replacing the verifier configuration
endpoint.
* A
[new endpoint](/docs/verification/certificates/api-reference/verifier-root-ca-certificates#create-a-verifier-root-ca-certificate)
is now available to create a verifier root CA certificate, replacing the verifier configuration
endpoint.
* When performing online verification of mDocs, the credential status is now returned in the
`status` property of the response (previously it was returned in `status.type`).
## Enhanced certificate capabilities in MATTR VII
2024-12-09
Tags: Issuance
This release of the MATTR VII platform (v5.0.0) introduces the following key enhancements to X.509
certificates capabilities in MATTR VII:
* To support IACA [rotation](/docs/concepts/chain-of-trust#certificates-rotation), tenants can now have multiple IACAs.
This allows issuers to create new IACAs and distribute them to relying parties in advance to
ensure continuous operation of issuance and verification workflows.
* Generation of
[Document Signer Certificates (DSCs)](/docs/issuance/certificates/overview#document-signer-certificate-dsc) is now
handled automatically by MATTR VII, ensuring issuance workflows don't break due to DSC expiry.
* DSCs now include a Subject Alternative Name field to improve their alignment with the
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html) standard.
Additional features included in this release:
* The
[online presentation cross-device modal](/docs/verification/remote-web-verifiers/workflow#the-link-is-used-to-invoke-a-compliant-digital-wallet)
now closes when the user clicks outside of it.
* Deprecation of the following Ecosystem endpoints:
* Retrieve Issuer's Policy.
* Retrieve Verifier's Policy.
* Retrieve Policy.
These endpoints were replaced by a single endpoint to retrieve a consolidated ecosystem policy.
## MATTR VII Management maintenance release
2024-12-09
Tags: Maintenance, Platform Management
This release of the MATTR VII management APIs (v1.6.0) is a maintenance release. It introduces
backend changes required to support exciting new features that are coming up.
## Improved user experience in the Self Service Portal
2024-12-03
Tags: Platform Management
The new version of the Self Service Portal (v1.52.0) introduces an enhanced navigation sidebar for
improved user experience. Features are now organized by user roles, enabling faster and more
intuitive access to portal functionalities.
The Self Service Portal is available to selected cloud environments. [Contact
us](https://mattr.global/contact) if you're interested in accessing these
features or learning more.
## MATTR VII Management maintenance release
2024-11-25
Tags: Maintenance, Platform Management
This release of the MATTR VII management APIs (v1.5.0) is a maintenance release. It introduces
backend changes required to support exciting new features that are coming up.
## mDocs revocation now available in MATTR VII
2024-11-25
Tags: Issuance
This release of the MATTR VII platform (v4.4.0) introduces support for mDocs revocation. Issuers can
now issue mDoc in a way that enables them to later change their status between `valid`, `suspended`
and `invalid`. Relying parties can retrieve this status and use it in their verification workflows.
Refer to mDocs [revocation](/docs/issuance/revocation/overview) for more information.
Additional features included in this release:
* [Document Signer Certificates (DSCs)](/docs/issuance/certificates/overview#document-signer-certificate-dsc) can now
be created with validity of up to 10 years.
* When attempting to sign an mDoc which has `DocType` set to `org.iso.18013.5.*.mDL` (where `*` is a
positive integer), MATTR VII recognizes that this is attempt to sign an mDL and will validate that
the DSC used to sign it isn't valid for more than 457 days to comply with
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
## Self Service Portal Maintenance release
2024-11-14
Tags: Maintenance, Platform Management
The new version of the Self Service Portal (v1.51.0) is a maintenance release. It includes backend
changes required for exciting new features that are coming up.
The Self Service Portal is available to selected cloud environments. [Contact
us](http://mattr.global/contact) if you're interested in accessing these
features or learning more.
## CRL distribution points now optional in MATTR VII
2024-11-11
Tags: Issuance, Trust Networks
This release of the MATTR VII platform (v4.3.0) introduces relaxed validation rules when adding
external IACAs. The following IACAs were previously rejected by MATTR VII but can now be accepted:
* IACAs without a Certificate Revocation List (CRL) distribution endpoint.
* IACAs without a `pathLenConstraint` basic constrain.
These IACAs can now be added as the root certificates of:
* Trusted mDoc issuers.
* Ecosystem participants.
## MATTR VII Management maintenance release
2024-11-11
Tags: Maintenance, Platform Management
This release of the MATTR VII management APIs (v1.4.0) is a maintenance release. It introduces
backend changes required to support exciting new features that are coming up.
## MATTR VII Management API maintenance release
2024-10-29
Tags: Maintenance, Platform Management
This release of the MATTR VII management APIs (v1.3.0) is a maintenance release. It introduces
backend changes required to support exciting new features that are coming up.
## MATTR VII Platform maintenance release
2024-10-29
Tags: Maintenance
This release of the MATTR VII platform (v4.2.0) is a maintenance release. It introduces backend
changes required to support exciting new features that are coming up.
## Removal of DSC functionalities from the Self Service Portal
2024-10-23
Tags: Platform Management, Issuance
This release of the Self Service Portal (v1.50.0) removes
[Document Signer Certificate (DSC)](/docs/issuance/certificates/overview#document-signer-certificate-dsc)
functionalities from the website, as these are going to be handled automatically by the MATTR VII
tenant.
Additionally, this release includes miscellaneous maintenance changes and bug fixes.
The [Self Service Portal](/docs/platform-management/portal) is available to
selected cloud environments. [Contact us](https://mattr.global/contact) if
you're interested in accessing these features or learning more.
## Terminology updates across MATTR platforms
2024-10-15
Tags: Maintenance
To make it easier to consume our capabilities, we have made some changes to our terminology to
describe credentials supported by MATTR platforms by their underlying technology and standards. The
following credential formats are supported:
* [mDocs](/docs/concepts/mdocs): Previously referred to as Mobile credentials.
* [CBOR Web Tokens (CWT) credentials](/docs/concepts/cwt): Previously referred to as Compact credentials.
* JSON credentials: Previously referred to as Web credentials.
## Ecosystem and certificate enhancements in MATTR VII
2024-10-14
Tags: Issuance, Trust Networks
This release of the MATTR VII platform (v4.1.0) introduces improvements to our
certificates and Ecosystem capabilities:
* When retrieving Ecosystem integrations, you can now view the time when the integration was last
synced at.
* Improved validation logic now prevents different IACA certificates that only differ by whitespaces
and/or line breaks.
## Management API maintenance release
2024-10-14
Tags: Maintenance, Platform Management
This release of the MATTR VII management APIs (v1.2.0) is a maintenance release. It introduces
backend changes required to support exciting new features that are coming up.
## VICAL Capabilities in the Self Service Portal
2024-10-10
Tags: Platform Management, Trust Networks
The new version of the [Self Service Portal](/docs/platform-management/portal) (v1.49.0) introduces
new certificate and [VICAL](/docs/digital-trust-service/vical-overview) management capabilities. You can
now use the portal to:
* Create [IACA](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca) certificates.
* Create and manage VICAL configurations.
* View more details when inspecting VICAL previews.
Additionally, this release includes miscellaneous maintenance changes.
The [Self Service Portal](/docs/platform-management/portal) is available to
selected cloud environments. [Contact us](http://mattr.global/contact) if
you're interested in accessing these features or learning more.
## Mobile Credentials online verification now available in MATTR VII
2024-09-30
Tags: Issuance, Verification
This release of the MATTR VII platform (v4.0.0) introduces new capabilities to support online
verification of Mobile Credentials, as per
[ISO/IEC 18013-7](https://www.iso.org/standard/91154.html).
You can now use a MATTR VII tenant together with the
[Verifier Web SDK](https://api-reference-sdk.mattr.global/verifier-sdk-web/latest/index.html) to
verify [Mobile Credentials](/docs/concepts/mdocs) presented online via both
[same-device](/docs/verification/remote-overview#same-device-verification-workflow) and
[cross-device](/docs/verification/remote-overview#cross-device-verification-workflow) flows.
Learn more about the [online verification flow](/docs/verification/remote-overview) and its
[implementation](/docs/verification/remote-web-verifiers/workflow).
## Enhanced certificate parsing
2024-09-25
Tags: Issuance
This release of the MATTR VII platform (v3.9.1) introduces an improvement to the parsing of X.509
certificates, so that the certificate's `issuingAuthority` can now be displayed in a human readable
format.
## Expanded Ecosystem capabilities in the Self Service Portal
2024-09-17
Tags: Platform Management, Trust Networks
The new version of the Self Service Portal (v1.48.0) introduces new
[Ecosystem](/docs/digital-trust-service) and [certificates](/docs/concepts/chain-of-trust) management
capabilities. You can now use the Self Service Portal to:
* Create, update and delete an
[Ecosystem](/docs/digital-trust-service/vical-guide#create-an-ecosystem).
* View and download previously generated [VICALs](/docs/digital-trust-service/vical-overview).
* View the `stateOrProvinceName` and `country` fields for
[IACAs](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca) and
[DSCs](/docs/issuance/certificates/overview#document-signer-certificate-dsc).
The Self Service Portal is available to selected cloud environments. [Contact
us](http://mattr.global/contact) if you're interested in accessing these
features or learning more.
## New ecosystem capabilities and online presentation tech preview
2024-09-16
Tags: Verification, Trust Networks
This release of the MATTR VII platform (v3.9.0) introduces the following new capabilities:
* [Ecosystem](/docs/digital-trust-service) operators can now integrate external trusted
sources into their own ecosystem policy. These can be either a different Ecosystem or a
[VICAL](/docs/digital-trust-service/vical-overview). These external sources are then integrated into the
[ecosystem policy](/docs/digital-trust-service/vical-guide#manually-publish-a-vical) when it is published and
consumed by relying parties.
* Various enhancements required for the tech preview of Mobile Credentials online presentation.
[Contact us](https://mattr.global/contact-us) if you are interested in this capability.
## MATTR VII Maintenance release
2024-08-29
Tags: Maintenance
This release of the MATTR VII platform (v3.8.0) is a maintenance release. It introduces backend
changes required to support exciting new features that are coming up.
## New Ecosystem capabilities in the Self Service Portal
2024-08-29
Tags: Platform Management, Trust Networks
The new version of the Self Service Portal (v1.47.0) introduces new
[Ecosystems](/docs/digital-trust-service) management capabilities. Ecosystem operators can
now use the Self Service Portal to:
* Manage
[Ecosystem participants](/docs/digital-trust-service/vical-guide#create-participants-and-add-issuer-certificates).
* Preview and generate a [VICAL](/docs/digital-trust-service/vical-overview) based on their
[Ecosystem policy](/docs/digital-trust-service/vical-guide#manually-publish-a-vical).
The Self Service Portal is available to selected cloud environments. [Contact
us](http://mattr.global/contact) if you're interested in accessing these
features or learning more.
## Enhanced Ecosystem participant validation
2024-08-19
Tags: Trust Networks
This release of the MATTR VII platform (v3.7.0) introduces enhanced Ecosystem
[participants](/docs/digital-trust-service/vical-guide#create-participants-and-add-issuer-certificates) validation.
With this update, when a participant is
[created](/docs/api-reference/platform/participants/createEcosystemParticipant)
with a `country` and/or `stateOrProvince` fields, these must match the corresponding fields in the
IACA certificate used as the `mobile` identifier for this participant.
## Self Service Portal Maintenance release
2024-08-13
Tags: Maintenance, Platform Management
The new version of the Self Service Portal (v1.46.0) is a maintenance release. It tidies up various
UI elements, hint texts and external links, while introducing some backend changes required for
exciting new features that are coming up.
The Self Service Portal is available to selected cloud environments. [Contact
us](http://mattr.global/contact) if you're interested in accessing these
features or learning more.
## Updates to Mobile Credential configurations claim types
2024-08-12
Tags: Issuance
This release of the MATTR VII platform (v3.6.0) introduces the following changes to the Mobile
Credential `org.iso.18013.5.1.driving_privileges` claim type:
* Mapping data into either the `issue_date` or `expiry_date` items in the
`org.iso.18013.5.1.driving_privileges` claim is now optional.
* You can now map data into a `codes` array within the `org.iso.18013.5.1.driving_privileges` claim.
This enables including specific driving privileges code information in the Mobile Credential.
## Enhancements and improvements across different MATTR VII capabilities
2024-08-05
Tags: Issuance, Trust Networks
This release of the MATTR VII platform (v3.5.0) includes several enhancements and features, which
now enable you to:
* Issue [Compact Credentials](/docs/concepts/cwt) in a [revoked](/docs/issuance/revocation/overview) state, so that they
only become valid once you manually change their revocation status. Refer to the
[Compact Credentials direct issuance guide](/docs/issuance/cwt-direct-issuance) for more information.
* Populate new fields when creating
[Ecosystem participants](/docs/digital-trust-service/vical-guide#create-participants-and-add-issuer-certificates).
These optional fields include the participant's status (active/inactive), country, state/province
and contact details.
* Define an issuer's state/province when creating their
[IACA](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca), as per
[ISO 18013-5](https://www.iso.org/standard/69084.html). Refer to the
[IACA creation guide](/docs/issuance/certificates/guide) for more information.
In addition to these new features, in this release we have introduced alternative paths to some of
our existing endpoints, where we added hyphens for better readability. This is not a breaking change
as the non-hyphenated paths are still supported. However, in accordance with the terms of our
[Service Level Agreement (SLA)](/docs/resources/terms/service-level-agreement), the non-hyphenated paths are
now marked as **"Retired"**, and will reach their EOL on February 5th 2025, when they will be
removed from the MATTR VII platform. You are advised to adjust your implementation to use the
following new paths prior to the EOL date:
* Use `/v2/credentials/compact/digital-pass/apple` to replace
`/v2/credentials/compact/digitalpass/apple`.
* Use `/v2/credentials/compact/digital-pass/apple/templates` to replace
`/v2/credentials/compact/digitalpass/apple/templates`.
* Use `/v2/credentials/compact/digital-pass/google` to replace
`/v2/credentials/compact/digitalpass/google`.
* Use `/v2/credentials/compact/digital-pass/google/templates` to replace
`/v2/credentials/compact/digitalpass/google/templates`.
* Use `/v2/credentials/compact-semantic/digital-pass/apple` to replace
`/v2/credentials/compact-semantic/digitalpass/apple`.
* Use `/v2/credentials/compact-semantic/digital-pass/apple/templates` to replace
`/v2/credentials/compact-semantic/digitalpass/apple/templates`.
* Use `/v2/credentials/compact-semantic/digital-pass/google` to replace
`/v2/credentials/compact-semantic/digitalpass/google`.
* Use `/v2/credentials/compact-semantic/digital-pass/google/templates` to replace
`/v2/credentials/compact-semantic/digitalpass/google/templates`.
* Use `/v1/users/authentication-providers` to replace `/v1/users/authenticationproviders`.
* Use `/v1/claim-sources` to replace `/v1/claim-sources`.
* Use `/v2/credentials/mobile/document-signers` to replace
`/v2/credentials/mobile/document-signers`.
* Use `/v2/credentials/web-semantic/linked-data/convert` to replace
`/v2/credentials/web-semantic/linkeddata/convert`.
## MATTR VII Management API release
2024-08-01
Tags: Platform Management
The new version of the MATTR VII Management API (v1.1.0) introduces new internal capabilities to
facilitate tenant off-boarding.
Please [contact us](http://mattr.global/contact-us) if you are interested in these capabilities.
## Self Service Portal Maintenance release
2024-07-25
Tags: Maintenance, Platform Management
The new version (v1.45.0) of the Self Service Portal is a maintenance release.
The Self Service Portal is available to selected cloud environments. [Contact
us](http://mattr.global/contact) if you're interested in accessing these
features or learning more.
## Enhanced validation and certificate management in MATTR VII
2024-07-22
Tags: Issuance
This release of the MATTR VII platform (v3.4.0) includes the following enhancements:
* Binary [claim types](/docs/issuance/credential-configuration/overview#claims-mapping) in
[Mobile Credential configurations](/docs/issuance/credential-configuration/overview) are now
validated to be in a `base64` format. This creates a more robust solution, as binary claim types
that are formatted differently would cause credential issuance to fail.
* Deleting [IACA certificates](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca) now
returns an error if any of the
[DSC child certificate](/docs/issuance/certificates/overview#document-signer-certificate-dsc) keys were not removed.
## MATTR VII Management API security enhancements
2024-07-17
Tags: Maintenance, Platform Management
The Management API offers a set of actions beyond the scope of a single tenant or environment. This
release includes an upgrade of several dependencies to mitigate potential security vulnerabilities.
## Improved x509 certificate validations and usability in MATTR VII
2024-07-08
Tags: Issuance
X.509 certificates are a standardized format for digital certificates.
[IACAs](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca) and
[DSCs](/docs/issuance/certificates/overview#document-signer-certificate-dsc), used to sign and verify Mobile
Credentials, are both X.509 certificates. This release of the MATTR VII platform (v.3.3.0) improves
validation and usability of X.509 certificates:
* Uploaded PEM certificates are now validated. New IACAs and DSCs cannot be created with invalid
PEMs.
* DSC expiry dates (`notBefore` and `notAfter`) are now parsed to ensure they match the required
`Date` type.
* By default DSCs can be created with a maximal validity of 457 days. You can now request to adjust
this limit on a per-tenant basis.
## Certificates now available in the Self Service Portal
2024-07-04
Tags: Platform Management, Issuance
You can now use the Self Service Portal to view certificates available on your tenant. This includes
[DSCs](/docs/issuance/certificates/overview#document-signer-certificate-dsc) and
[IACAs](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca) which are used to issue and
verify [Mobile Credentials](/docs/concepts/mdocs).
The Self Service Portal is available to selected cloud environments. [Contact
us](http://mattr.global/contact) if you're interested in accessing these
features or learning more.
## Improved did:web interoperability
2024-06-27
Tags: Issuance
This release of the MATTR VII platform (v3.2.0) improves `did:web` interoperability by supporting
usage of a single `did:web` by multiple issuance systems, all issuing credentials on behalf of a
single credential issuer.
## Security enhancements and bug fixes
2024-06-24
Tags: Maintenance
This release of the MATTR VII platform (v3.1.0) enhances security of integrating Claims sources by
extending the current URL validation to also cover any redirects that are included in a configured
Claims source URL.
This release also includes miscellaneous bug fixes and usability enhancements, as well as required
modifications to support future functionalities.
## Introducing Webhook enhancements, the Events registry and Semantic versioning
2024-06-10
Tags: Platform Management, Issuance, Verification
### Webhook enhancements [#webhook-enhancements]
The following enhancements to the Webhook capabilities are now available:
The event payload now includes a `userClaims` object, which contains all the claims persisted on
your tenant as part of the issuance workflow. Refer to
[Webhooks payload](/docs/platform-management/webhooks-guide#event-payload) for more information.
You can now subscribe to a new `OpenIdCredentialIssuedSummary` event type. This event is triggered
upon the completion of an [OID4VCI issuance flow](/docs/issuance/oid4vci-overview) but only provides a
summary of the issuance event, leaving out the `credential` object. Refer to
[Webhooks](/docs/platform-management/webhooks-overview) for more information.
The [endpoint](/docs/platform-management/webhooks-api-reference#retrieve-webhook-jwks) that is used to retrieve
public keys MATTR VII uses to sign the Webhook HTTP requests is now publicly available. This makes
it easier for relying parties to verify incoming Webhook requests. Refer to
[Verify a Webhook](/docs/platform-management/webhooks-guide#verify-webhook-requests) for more information.
### Events registry [#events-registry]
The [Events registry](https://api-reference-sdk.mattr.global/event-registry/latest/index.html) is a
publicly available comprehensive collection of analytic events generated by the MATTR VII platform.
Events are grouped by the service that generates them, which corresponds to the event category. The
registry details the structure of different event payloads based on the configured logging level.
Refer to [Events structure](/docs/platform-management/analytics/overview#events-structure) for more
information.
### Semantic versioning [#semantic-versioning]
To increase transparency and simplify support and maintenance workflows, this release introduces
versioning to MATTR VII, following the [Semantic Versioning](https://semver.org/) specification.
This current release is tagged as version `3.0.0`.
## Configure a Webhook in the Self Service Portal
2024-06-06
Tags: Platform Management
You can now use the MATTR VII [Self Service Portal](/docs/platform-management/portal) to configure a
[Webhook](/docs/platform-management/webhooks-overview) for a tenant in your environment.
You can subscribe to specific events that are triggered on set MATTR VII operations to retrieve the
required information whenever it is generated.
The Self Service Portal is available to selected cloud environments. [Contact
us](http://mattr.global/contact) if you're interested in accessing these
features or learning more.
## Configure a Custom domain in the Self Service Portal
2024-05-21
Tags: Platform Management
You can now use the MATTR VII [Self Service Portal](/docs/platform-management/portal) to configure a
[Custom domain](/docs/platform-management/custom-domain-overview) for a tenant in your environment.
Custom domains represent your known and trusted brand, and can assist in instilling trust with your
end-users when they interact with your MATTR VII tenant.
Custom domains don't change how you interact with your tenant for administration functions and don't
prevent the existing tenant domain from being accessed.
The Self Service Portal is available to selected cloud environments. [Contact
us](http://mattr.global/contact) if you're interested in accessing these
features or learning more.
## End to end OID4VCI support in the Self Service Portal
2024-05-06
Tags: Platform Management, Issuance
The MATTR [Self Service Portal](/docs/platform-management/portal) now supports configuring your
[OpenID for Verifiable Credentials Issuance (OID4VCI)](/docs/issuance/oid4vci-overview) workflow:
* Creating an [Authentication Provider](/docs/issuance/authorization-code/authentication-provider/overview)
configuration.
* Configuring an [Interaction hook](/docs/issuance/authorization-code/interaction-hook/overview).
* Configuring [Claims sources](/docs/issuance/claims-source/overview).
* Creating Compact, Compact Semantic, Web and Mobile
[Credential configurations](/docs/issuance/credential-configuration/overview).
* Creating and sharing [Credential offers](/docs/issuance/credential-offer/overview).
The Self Service Portal is available to selected cloud environments. [Contact
us](http://mattr.global/contact) if you're interested in accessing these
features or learning more.
## Credential issuance enhancements
2024-04-15
Tags: Issuance
We have introduced new capabilities to support more issuance use cases, as well as improve
integration capabilities.
### Key Features [#key-features]
* You can now add an `issuanceDate` parameter to a signed Web Credential. This means the issued
credential will only become valid once `issuanceDate` is in the past. This is only available for
direct issuance of JSON credentials, and not via the
OID4VCI workflow.
* You can now pass objects and arrays as request parameters when
[configuring a Claims source](/docs/issuance/claims-source/overview) for your
[OID4VCI](/docs/issuance/oid4vci-overview) workflow. This means you can simplify integration with your
existing data sources.
## Introducing Enhanced Ecosystems
2024-01-24
Tags: Trust Networks
[Ecosystems](/docs/digital-trust-service) are part of the MATTR VII platform. They enable
service providers to define policies regarding valid issuers, credential types and verifiers.
This release introduces the following MATTR VII ecosystem capabilities:
* Create an ecosystem: Create an ecosystem to act as the overarching entity that would include all the other components.
* Create valid participants: Once an ecosystem is in place, you can create valid participants that can be trusted within it. Participants can be issuers and/or verifiers.
* Configure valid credential types: Configure what credential types are valid in the ecosystem.
* Create a policy: Create policies that define what participants are allowed to issue/verify what credentials in the ecosystem.
* Retrieve a policy: Retrieve the ecosystem policies and use the information within them to apply your own business logic.
Refer to our [Docs](/docs/digital-trust-service) to learn more about Ecosystems.
## Introducing Mobile Credentials
2023-11-08
Tags: Issuance
We are thrilled to expand our credential profile suite by introducing support for
[Mobile Credentials](/docs/concepts/mdocs). These are digital identity documents that are designed to be
stored on the holder’s mobile devices. They offer a range of unique capabilities, making them an
ideal choice for use cases which require higher assurance identity credentials, such as driving
licenses or national IDs.
Mobile Credentials are a MATTR VII implementation of the
[ISO 18013-5:2021 specification](https://www.iso.org/standard/69084.html), created to standardize
Mobile Driver Licenses (mDLs). When added to the digital trust ecosystem, Mobile Credentials can be
applied to a wider variety of use cases and business problems.
Our APIs offer the following features to support Mobile Credentials:
* Signing Mobile Credentials and issuing them to holders.
* Verifying Mobile Credentials using our Verifier SDKs.
* Managing
[Issuing Authority Certificate Authorities (IACAs)](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca)
and [Document Signer Certificates (DSCs)](/docs/issuance/certificates/overview#document-signer-certificate-dsc).
Refer to our Docs to learn more about [Mobile Credentials](/docs/concepts/mdocs) and how they can be embedded
into your digital trust ecosystem.
## Automatic Revocation when deleting credential data
2023-10-26
Tags: Issuance
We’ve made a change to our credential deletion process so that any revocable credential will be
automatically revoked when it is deleted from the MATTR VII Credential Registry. This creates an
easier way to revoke and delete credentials by merging the two API requests into one, and also
prevents deleting the credential data and losing Credential ID needed to revoke it later.
Note that this change only applies to issued credentials that were configured to be revocable, and
you can still update the revocation status for credentials without deleting them using the existing
Revocation endpoints:
* Update Revocation Status for a Web Credential
* [Update Revocation Status for a Compact Credential](/docs/issuance/revocation/api-reference/cwt-status-update#update-cwt-credential-status)
## Removed support for DID:ION
2023-10-25
Tags: Maintenance
From 25 October onwards, the `did:ion` method is no longer supported by the MATTR VII platform. This
is the result of 3rd party providers no longer reliably supporting the required blockchain utilized
by this method. This method was a trial capability.
We apologize for any inconvenience and we welcome you to [reach out](http://mattr.global/contact-us)
to us and discuss alternative supported DID methods such as `did:web` and `did:key`.
## Enhancements to Claims Sources Configuration
2023-09-05
Tags: Issuance
We are excited to announce a suite of enhancements to
[Claims sources](/docs/issuance/claims-source/overview) configuration in MATTR VII. These
enhancements enable customers to better fine tune how MATTR VII interacts with their claims source:
* Choose your preferred query method: You can now use both `POST` and `GET` when querying the claims
source. = Choose your preferred authentication method: You can now authenticate with your claims
source using either an API Key or your OAuth client credentials.
* Query what you need: You can now query the claims source using a credential configuration as a
query parameter.
Refer to the Docs to learn more about [Claims sources](/docs/issuance/claims-source/overview).
## New Features and Enhancements across MATTR platforms
2023-08-03
Tags: Issuance, Holding
We are happy to announce several key improvements across our MATTR platforms, highlighted by new
self-service capabilities, enhanced APIs, and several MATTR Wallet features:
### Self-service Tenant Management [#self-service-tenant-management]
Our new suite of API endpoints support the ability to manage tenants (create, view and delete) and
analytics events in your environment. Please [contact us](mailto:dev-support@mattr.global) for more
information and/or access to this new capability.
### Preventing Issuance of Expired Credentials [#preventing-issuance-of-expired-credentials]
Our MATTR VII credential issuance API endpoints now prevent issuing any expired credentials where
the current date has passed the specified expiry date.
### MATTR Wallet and MATTR GO Release [#mattr-wallet-and-mattr-go-release]
Our recent MATTR Wallet release (V2.6.1) introduces support for claiming and storing Compact
Credentials issued through the OID4VCI protocol. In addition, it includes several UI enhancements
to further improve the wallet user experience.
**MATTR GO Wallet** customers will receive a new release which includes all new features and
enhancements from our latest MATTR Wallet version.
## Enhancements and new features across MATTR platforms
2023-07-07
Tags: Issuance, Holding
We're excited to announce the following updates and new features across our MATTR platforms:
* You can now issue Compact Credentials using the OpenID Credential Provisioning flow. This includes
using all the features that this flow enables, such as integration hooks, claims source
integration and multi-credential issuance for Compact Credentials.
* We've enabled the MATTR Pi Wallet Toolkit with the capability to retrieve and hold Compact
Credentials.
* We now support multiple key types within a single DID, which means you can issue credentials in
multiple Credential Profiles using the same DID.
## Enrichment of verification responses
2023-05-10
Tags: Verification
We've released an enhancement to the way our [verification](/docs/verification) capabilities return
the verified credential information. Until now, MATTR VII has applied a layer of convenience for
integrations by returning only the claims from the verified credential.
You can now also get the raw credential presentation shared by the holder in the verification
response. This brings more information and options to verifiers that enable subsequent flows like:
* Re-verifying a credential using the MATTR VII verify capabilities, to re-check things like the
[revocation](/docs/issuance/revocation/overview) status.
* Obtaining more information about which credential data attributes are coming from which credential
when the verifier requested more than one credential.
Check out our Docs for more details on how to enable and use this enhanced capability in your
[credential verification flows](/docs/verification).
## Terms
2022-11-10
Tags: Platform Management
We have an updated [Privacy Policy](/docs/resources/terms/privacy-policy) now in place.
## Introducing webhooks
2022-10-25
Tags: Platform Management, Verification
We have added support for [Webhooks](/docs/platform-management/webhooks-overview) to MATTR VII.
This new capability allows users to obtain information that is generated during an API operation
that isn't otherwise available as part of the request or response payloads.
Users are able to subscribe to specific events that are triggered on set MATTR VII operations.
When an event is triggered, the information relating to that event is published via the webhook
through to the URL(s) set up on the configured subscription(s).
Users can now:
* Create a webhook that is triggered on supported event types.
* Verify a webhook to check the integrity and authorship
of webhooks generated by the MATTR VII platform.
Interested in learning more about how you might use the MATTR VII Platform?
[Get in touch](http://mattr.global/contact-us) with us today.
## MATTR VII - Event logs
2022-08-15
Tags: Platform Management
MATTR introduces enhanced platform ops logging levels on MATTR VII.
As of today, we support configuration of logging at the platform environment level along with manual
consumption of platform events in specific customer environments.
In future customers will be able to customize these levels more freely and 'fan-out' events to other
operational systems via APIs and webhooks.
New logging levels supported:
* Level 1 - Basic fields
* Level 2 - Metadata + basic fields
* Level 3 - Data (full request and response payloads) + metadata + basic fields
All MATTR VII public cloud environments (and associated tenants) are set to
Level 1 - Basic Fields. No personal identifiable information (PII) is being
captured in event logs at this level.
Interested in learning more about how you might use the MATTR VII Platform?
[Get in touch](http://mattr.global/contact-us) with us today.
## OIDC Bridge - Additional configurations
2022-08-10
Tags: Issuance
In this release, we added support for including the following configurations when setting up an OIDC
Credential Issuer.
* **federatedProvider.claimSource** is either `idToken` (default) `or` userInfo
* **federatedProvider.tokenEndpointAuthMethod** is either `client_secret_post` (default), or
`client_secret_basic`
* **staticRequestParameters**: parameters that should be included in the request to the IDP. i.e.
`display`, `prompt`, `max_age`, `ui_locales` etc.
* **forwardedRequestParameters**: parameters that can be provided by the client to be forwarded to
the IDP. These are optional and can override the staticRequestParameters i.e. `login_hint`.
We've also updated our [MATTR Wallet SDK](/docs/holding/sdk-overview) and
[MATTR Wallet App](/docs/holding/go-hold/getting-started) to include `login_hint` as a request parameter when issuing a
credential using the OIDC bridge. This will allow pre-population of the username in the Federated
Provider's login screen when using MATTR Wallet to claim a credential. Any other request parameters
are not supported by MATTR Wallet and SDK at the moment.
Interested in learning more about how you might use the MATTR VII Platform?
[Get in touch](http://mattr.global/contact-us) with us today.
## New MATTR VII Regions
2022-08-01
Tags: Platform Management
MATTR VII is now available in two additional AWS regions:
* Frankfurt, Germany
* Montréal, Canada.
## Introducing compact credentials
2022-06-30
Tags: Issuance
Claims of data can be represented as [Compact Credentials](/docs/concepts/cwt), which are both
cryptographically proven as authentic and dense enough to fit inside a QR code. This credential
format is ideal where high information assurance is required but not high identity assurance about
the entity presenting the credential.
You can choose to use either a W3C Verifiable Credential data model to provide more descriptive
semantic meaning or a more concise, non-semantic data model. The choices between the data model to
use comes down to how compact you need the credential to be versus how openly you intend to share
and exchange the created credentials across different domains and jurisdictions.
The following capabilities of Compact Credentials are provided in this MATTR VII platform release:
* Sign and issue a Compact Credential in the semantic model or compact model
* Verify a Compact Credential.
* [Revoke](/docs/issuance/revocation/overview) a Compact Credential.
* [Format the Compact Credential](/docs/issuance/cwt-direct-issuance#format-the-signed-cwt-credential) in a
way that allows it to be presented in either a digital or paper-based manner.
With this product release, our [Customer Agreement and
Terms](/docs/resources/terms/customer-agreement) have changed. Please refer to
the version dated 30 June 2022 for details.
## Compressed credential support (technical preview)
2022-03-24
Tags: Issuance
Compressing semantically verifiable credentials into smaller payload sizes is a useful technique.
For example, it allows credentials and presentations to be embedded into QR codes so they can be
used when one party is offline.
Try out our latest technical preview on compressed credentials to see how using CBOR-LD can unlock
use cases where offline is important.
Convert JSON-LD to CBOR-LD to compress the payload size
Use the latest version of the [MATTR Wallet](/docs/holding/go-hold/getting-started) (v1.9.1) to present applicable
credentials in a CBOR-LD format
Convert CBOR-LD payloads to JSON-LD to use with existing MATTR VII API
## Digital Covid Certificate (DCC) Extension
2021-11-05
Tags: Issuance
We have introduced a new DCC extension to MATTR VII that is built on top of our core libraries to
provide the capability to issue and verify Digital Covid Certificates (DCC).
The standards outlined for the European Union DCC (EUDCC) format, which covers 3 certificate types
(vaccination, recovery, and testing) are all covered by the extension which allows your MATTR VII
tenant the ability to:
* Maintain the required document signer certificates that facilitate trusted issuance and
verification of the EUDCC format.
* Sign and issue a health certificate payload into a EUDCC format
* Verify a EUDCC
* Format the EUDCC in a way that allows it to be presented in either a digital or paper-based
manner.
The use of the DCC extension during a trial of the MATTR VII platform may be
subject to change. As you move into production workloads please get in touch
to discuss your needs.
## Introducing the New Zealand COVID Pass (NZCP) Verify Extension
2021-11-05
Tags: Issuance, Verification
The New Zealand government will start issuing a type of digital health certificate known as a 'My
Vaccine Pass' using the
[New Zealand COVID Pass (NZCP) specification](https://github.com/minhealthnz/nzcovidpass-spec) ,
this credential contains a limited set of personal information and provides a way for the holder to
prove they meet certain health policy requirements in regards to COVID-19 such as being vaccinated
against the virus.
From today you can now read about the NZCP Verifier API to help you determine how to integrate and
verify NZ COVID Passes that have been presented to you, this also accompanies the NZCP Verifier SDK
and Verifier white label app offerings.
[Get in touch](http://mattr.global/contact-us) to start onboarding to use the service today, the API
will also be available on a trial basis starting soon.
## ZKP-enabled credentials using Web DIDs & support for custom paths
2021-10-22
Tags: Issuance
This release adds the ability to use `bls12381g2` key types with a [Web DID](/docs/concepts/dids#didweb)
so that ZKP-enabled credentials can be issued. We have also enabled Web DIDs to be created on custom
paths that don't rely on a /.well-known location.
* Create a DID with the web method and a `bls12381g2` key type.
* A new `url` parameter in `options` to specify a domain for the Web DID as well as allowing the use
of paths in the form of `organization.com/path`.
The `domain` options parameter has now been functionally superseded by the
`url` option parameter and will be deprecated in an upcoming release.
## Introducing ION DIDs & an update on Sovrin DIDs
2021-09-08
Tags: Issuance, Verification
Decentralized Identifiers (DIDs) using the
[Identity Overlay Network (ION)](https://identity.foundation/ion/) method can now be created on the
platform and used for issuing credentials and other purposes. ION DIDs use the Sidetree protocol to
anchor the DID document to a ledger, which provides a high-throughput and efficient method for
writing to a blockchain like Bitcoin. ION DIDs can be easily configured on the MATTR VII platform
using our API interface, allowing you to leverage the benefits without having to deal with any of
the underlying complexities:
* Create & manage ION DIDs on your tenant
* ION DIDs can be used to create credentials, sign and encrypt messages as well as being fully
configurable on the OIDC Bridge for issuance and verify
* Supports `ed25519` and `bls12381g2` key types
* Fully resolve ION DID Documents from the public nodes
The creation of ION DIDs during a trial of the MATTR VII platform may be
subject to change. As you move into production workloads please get in touch
to discuss your needs.
### Sovrin DID method [#sovrin-did-method]
Since launching the platform our implementation of `did:sov` has relied on private Indy nodes whilst
the community around DID Sovrin continued to develop new kinds of interoperability in their
infrastructure. Recently activity is showing that rather than converging around the Sovrin-specific
method that’s been used to date new approaches are being looked at. Until this direction from the
community has more clarity around implementation we have decided to deprecate our current private
node support.
From this release, we will begin phasing out support for DIDs based on Sovrin by removing references
from the documentation and in the next release, we will stop the current `did:sov` support and
remove any Sovrin DIDs from the sandbox platform.
## Custom domains & complex credentials
2021-07-02
Tags: Platform Management, Verification, Holding
Tenants can now be configured to represent as a verified custom domain:
* [Custom domains](/docs/platform-management/custom-domain-overview) are a paid feature, setting up a custom domain
whilst using a sandbox is possible, however, note this may be disabled and reverted back at
MATTR's discretion.
* [New endpoints](/docs/platform-management/custom-domain-api-reference#create-a-custom-domain) added to
create, view, delete and verify a custom domain on your tenant
* [Create a custom domain](/docs/platform-management/custom-domain-guide) by providing details like your
organization name, domain and a logo which will be displayed to end-users interacting with your
tenant using a wallet app that supports a web manifest payload.
* The [MATTR mobile wallet](/docs/holding/go-hold/getting-started) app has been updated to support the display of custom
domains as well as a number of improvements to the UI of MATTR Wallet to be more human-friendly,
including support for more complex data types like nested data and embedded images.
## Create & Verify Presentations Directly
2021-05-20
Tags: Verification
New endpoints provided to help you work with verifiable presentations directly on MATTR VII:
* [Verify a presentation](/docs/verification) obtained from any source adhering to the
[W3C Verifiable Credential Data Model](https://www.w3.org/TR/vc-data-model/#presentations).
* Create verifiable presentations using Credentials where the subject(s) are controlled by the tenant
This is a useful operation for exploring how verifiable presentations are created and can be
submitted to the Verify a presentation endpoint.
An optional `description` parameter has been added when creating credentials:
* The optional `description` field is enabled on the Create Credential endpoint.
* The field can be configured in the OIDC Bridge Issuer so that any credentials issued will contain
the description.
The description field is part of the W3C Verifiable Credential Model v2
specification and will be supported in the MATTR mobile wallet as the standard
begins to stabilize.
## MATTR VII launch with push notification messaging
2021-03-25
Tags: Issuance
MATTR VII is now live!
### Pricing [#pricing]
Pay-as-you-go pricing is now published
* Get a detailed look at how MATTR VII is charged once you elect to upgrade to a paid plan.
* To discuss high-volume discounts, please [contact us](http://mattr.global/contact-us).
### API references [#api-references]
The platform is now known as MATTR VII; URLs and paths updated to reflect this:
* MATTR VII Core is `https://tenant.vii.mattr.global/core/v1`.
* OIDC Bridge is a MATTR VII extension found at `https://tenant.vii.mattr.global/ext/oidc/v1`.
Old domains and paths will be discontinued from service within 30 days.
### Notification messaging [#notification-messaging]
Customers can use their tenant to construct and send messages to holders based on their subject DID,
which will be delivered to the MATTR Wallet app and notified via a push notification.
* Construct action-based messages in a DIDComm2 JWM format:
* Start a credential issuance using the OIDC Bridge.
* Notify of a credential revocation status change.
* Start a verification flow using a callback.
* Encrypt messages intended for the recipient.
* MATTR VII enforces end-2-end encryption (E2EE), so message contents are never visible to MATTR
when held in messaging inboxes.
* Route messages to a dedicated inbox for the wallet user.
The MATTR Wallet app is being updated to support receiving push notification and managing messaging
inboxes. Make sure you update to the latest version available on the
[App Store](https://apps.apple.com/us/app/mattr-wallet/id1518660243) or
[Google Play](https://play.google.com/store/apps/details?id=global.mattr.wallet).
Further messaging capabilities are scheduled on the roadmap.
### Terms [#terms]
New customers signing up to MATTR VII will have a new customer agreement, SLA and privacy policy in
place.
## Verify ZKP-enabled Credentials
2021-02-18
Tags: Issuance, Verification
Further functionality to support the use of privacy-preserving credentials using BBS+ signatures.
### Create a JSON-LD Frame Presentation Request [#create-a-json-ld-frame-presentation-request]
* Use a query extension to the Verifiable Presentation Request Specification format, Query by Frame,
to specify required credential claims.
* Trusted Issuers and Credential Types are used to match credentials in the mobile wallet.
### Mobile Wallet updates [#mobile-wallet-updates]
* The latest version (v0.50.0) of the Mobile Wallet is required to process Query by Frame
presentation requests.
* ZKP-enabled credentials using BBS+ signatures can be used to derive selectively disclosed
presentations.
* New UI screens to actively show the disclosure of claims.
## Maintenance
2021-02-11
Tags: Issuance
Maintenance Release
* Update to the Callback URL for all Issuers on the OIDC Bridge to align with future changes.
Ensure that the allowed callback URL for your federated provided is updated with the new path. From
`../oidc/v1/issuers/..` To: `../ext/oidc/v1/issuers/..`.
## OIDC Bridge and OIDC Credential Provider
2020-12-16
Tags: Issuance
When we first launched the Platform we pioneered the bridging of existing identity solutions using
Open ID Connect (OIDC) to a new world of decentralized identity and verifiable credentials. During
this time we listened to customers as well as working within the Community as standards evolve. This
latest version of the OIDC Bridge is now easier to set up, more flexible to integrate and conforms
with [OIDC Credential Provider](https://mattrglobal.github.io/oidc-client-bound-assertions-spec/)
for issuing credentials to the mobile wallet.
### OIDC Bridge [#oidc-bridge]
* Multiple OIDC Credential Issuers can be enabled to offer credentials using the OIDC Configuration
metadata endpoints
* Custom `scopes` can be added to Federated Providers to enable more flexibility in obtaining ID
token claims
* OIDC Credential Verifiers are easier to set
up and associated OIDC Clients can be listed and updated
* Authenticate a DID using OIDC Bridge introduces a new
way for OIDC Clients to obtain a Subject identifier that has been verified to come from the
holder.
* Claim mappings: OIDC claims > JSON-LD terms and JSON-LD
terms > OIDC claims have been revamped to simplify their use and make it clearer on how they are
used by the OIDC Bridge
## Maintenance
2020-11-17
Tags: Issuance, Verification
Maintenance release
In line with the [W3C VC Data Model](https://www.w3.org/TR/vc-data-model/); Subject identifiers are
now not required on Create Credential, usually a Subject DID makes up a core part of a Verifiable
Credential but in some cases it makes sense without one, such as issuing a ‘bearer’ style credential
e.g. a concert ticket or when the credential is to be stored on behalf of a subject and reissued
later with subject binding.
## Maintenance
2020-11-04
Tags: Holding
Maintenance release
* The format of the response from
[`/.well-known/did-configuration`](/docs/api-reference/platform/dids/wellKnownDidConfig) is
now in a JSON-LD format. Learn more about the
[Well Known DID Configuration](https://identity.foundation/.well-known/resources/did-configuration/)
from the Decentralized Identity Foundation working group.
* This changes means all holders will need to being using the MATTR Mobile Wallet with a minimum
version of v0.37.1 to continue to receive and present credentials, earlier versions of the app
will present a generic error message.
## Revocable Credentials
2020-10-21
Tags: Issuance
Credentials issued on the platform are now revocable and searches can be performed on the Credential
Registry.
### Revocable Credentials [#revocable-credentials]
* Create Credential has new optional revocable property to create a Credential as revocable using a
revocation list method.
* All Credentials issued using the OIDC Bridge are now revocable by default.
* API endpoints for an Issuer to toggle the revoke status of a Credential.
* Provisioned hosting of revocation lists for Credential Issuers.
* Automatic verification of a presented Credential against its revocation list will result in
revoked credentials being returned with an error message in the OIDC/OAuth2 callback response back
to Verifiers/relying parties.
### Search on Credentials [#search-on-credentials]
* Credentials optionally held in the Credential Registry can now be retrieved by `tag` and `type` parameters.
* The meta-data of non-persisted Credentials can also be found using these tags.
* All Credentials issued using the OIDC Bridge will only store meta-data.
### Updates [#updates]
Pagination on [Retrieve List of DIDs](/docs/api-reference/platform/dids/retrieveListOfDids)
and Retrieve List of Presentation Templates now has pagination using the cursor-based method.
## DID Web Method
2020-10-07
Tags: Issuance
New [DID](/docs/concepts/dids) method [`did:web`](/docs/concepts/dids#didweb) is available to be created on
the Platform.
* Further content on the various DID methods available on the platform is available.
### Updates [#updates]
Enhanced pagination on the List Credentials endpoint, moved from using a page-offset pagination to a
much more performant cursor-based pagination.
## ZKP-Enabled Credentials
2020-09-16
Tags: Issuance
Support added for issuing privacy-preserving credentials using BBS+ signatures.
ZKP-enabled credential functionality during Preview are considered
experimental and may change over time as well as any ZKP-enabled credentials
issued during this period may need to be reissued.
### Create a DID with BLS Key Type [#create-a-did-with-bls-key-type]
* [Create DID](/docs/api-reference/platform/dids/createDid) now has options to set a key type
(only for `did:key` method at this time).
* Use the BLS key type `bls12381G2` to create Issuer DIDs for issuing ZKP-enabled credentials.
* Response for [Resolve a DID](/docs/api-reference/platform/dids/resolveDid) has been altered
to include a `localMetadata` parameter which will be used for future DID methods.
### Create a ZKP-enabled Credential [#create-a-zkp-enabled-credential]
Create Credential will automatically issue ZKP-enabled credentials if an `issuerDid` referencing a
`bls12381G2` key type is provided.
### Updates [#updates]
New optional parameters are available on Create Credential:
* Providing a value in `tag` will set this value as metadata so it can be referenced on the platform
later.
* Setting the `persist` boolean to `true` will store the created credential on your tenant for
future retrieval. The default value is not to store credentials.
## Maintenance
2020-09-09
Tags: Holding
Maintenance release.
* Mobile Wallet App bug fixes and improvements.
* Improved support for OIDC query parameters on mobile app authorization requests.
## Sovrin DID Method
2020-08-25
Tags: Issuance
Creation of Sov DIDs on the platform is now possible.
* [Create DID](/docs/api-reference/platform/dids/createDid) can be used to create DIDs using
the Sovrin DID method. Note during Preview these will not be anchored on the Sovrin MainNet.
* [Resolve a DID](/docs/api-reference/platform/dids/resolveDid) will resolve Sov DIDs
including MATTR issued ones.
## Maintenance
2020-08-10
Tags: Verification
Maintenance release
Tidy up of error response messages on Create Presentation Templates and messaging endpoints.
## Maintenance
2020-07-29
Tags: Maintenance
New endpoints available.
* Update operations now possible using Update a Claim Mapping and Update a Provider.
## Launch of the MATTR Preview Platform
2020-06-03
Tags: Issuance, Verification, Trust Networks, Holding
### SaaS Platform [#saas-platform]
* A cloud-hosted, multi-tenanted environment that can be spun-up on-demand using managed containers
* Authentication and access-control provisioning
* Auditing and privacy-preserving logging
### Issue Verifiable Credentials using OpenID Connect [#issue-verifiable-credentials-using-openid-connect]
* Cryptographically secure issuance of Verifiable Credentials (VC) to authenticated identity holders
* Configuration options to;
* Bring-your-own OpenID Connect Provider (OP)
* Or, use our step-by-step tutorial for a reference OP
* Map personal information claims from source to VC terms, using linked-data standards
* Decode a JWT signed using a Decentralized Identifier
* Optionally; store issued credentials on-platform to be retrieved (for non-sensitive use-cases)
* Create a credential Offer as a QR code or deep-link to start the issuance flow with the mobile
wallet app
* The static offer is ready to display on a website or physical media e.g. a bus shelter
advertisement
### Verify Verifiable Credentials using OpenID Connect [#verify-verifiable-credentials-using-openid-connect]
* Cryptographically secure verification of VCs from identity holders after their consent
* Uses the latest standards-based messaging protocols (JWM) to transmit information from the holder
* Configure an OpenID Connect Relying Party client to accept holder information via a standard ID
token
* Map personal information from Credential claims to a standard ID token
* Create a VC Request and embed using a QR code or deep-link into a journey
* The dynamic request can be used in an information-gathering flow e.g. Customer onboarding
### Mobile Wallet App [#mobile-wallet-app]
* Native iOS and Android apps, supporting a range of models and devices
* On-device biometric access enabled
* Familiar chat-like user-interface approach, designed with core pillars of privacy, accessibility
and user-experience
* Puts the user in control during issuance and verification of their Credentials
* Keeps user in-context with in-app-browser technology
* Interoperable to published specifications within the Self-Sovereign Identity ecosystem
* Theming options available to prospective customers
## DID Web hosting now on MATTR VII
Tags: Issuance
To help customers get started with using verifiable credentials quickly and easily, we now support
[DID Web hosting](/docs/concepts/dids#didweb) on the MATTR VII platform.
## Enhanced issuance journeys with OpenID Credential Provisioning
Tags: Issuance
We're thrilled to unveil the evolution of our credential issuance capabilities with the all-new
[OpenID Credential Provisioning flow](/docs/issuance/oid4vci-overview), based on the
[OpenID for Verifiable Credential Issuance (OID4VCI)](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html)
protocol.
This protocol is a key draft standard for interoperability among digital wallets and has been
included in the eIDAS expert group's draft
[European Digital Identity Architecture and Reference Framework (EUDI ARF)](https://mattr.global/resources/articles/demystifying-the-eudi-arf-part-one)
for digital wallets.
The new flow has evolved from our original OIDC Bridge credential issuance capabilities based on
market and community movements and feedback from customers. It simplifies the experience of
generating and configuring a credential for the issuer and it enhances the user experience of
collecting a credential.
## Enhancements and new features in the MATTR universe: April 2023
Tags: Issuance, Verification, Holding
The MATTR team has been busy the last few months! We have a raft of exciting new features and
updates coming to the MATTR platforms in April 2023.
* Next-generation credential issuance with our new OpenID Credential Provisioning flow, using the
OID4VCI standard.
* More flexibility for credential issuance with interaction hooks and claims source integration.
* DID Web hosting on the MATTR VII platform to simplify onboarding.
* Major changes to our MATTR VII API with a version 2 release.
* An update to the MATTR Wallet and Pi Wallet Toolkit to support an improved approach of matching
credentials to presentation requests from verifiers.
## New major changes to MATTR VII API
Tags: Maintenance
Continuing our theme of simplicity and ease of use, we will be releasing a new major version of our
API, which includes a new set of endpoints that simplifies the ability to utilize MATTR’s
[Credential Profiles](/docs/concepts/formats-overview).
Credential Profiles combine data about people, organizations or things with unique digital
signatures. We use different types of Credential Profiles depending on the type of information a
customer wants to convey and how they want to convey it.
See the \[API reference] for more information on these changes.
## Tools for extra flexibility in credential issuance
Tags: Issuance
Our \[OpenID Credential Provisioning
flow]\([OpenID Credential Provisioning flow](/docs/issuance/oid4vci-overview)) makes issuance easier than ever
before and we have built extra features that enable customers to have more flexibility to enact
their unique business logic into the flow. These include:
* **Interaction hooks**: integrate additional steps to the credential claiming journey such as
additional biometric checks, identity assurance flows, or informational screens.
* **Multi-credential issuance**: Issue multiple credentials to a wallet holder within a single user
journey.
* **Claims source integration**: Configure credentials using data from an existing source and
supplement with additional data from tenant-managed user claims as well as claims sourced from an
authentication provider or IDP.
More tools on the way soon!
For current customers, we will continue to support the OIDC Bridge for issuance through the end of
2023 to allow you to transition to the new protocol and feature set.
## Verify Credentials without using OIDC Bridge
Tags: Issuance, Verification
* Unlocks Verifying a Credential using a Callback method to allow non-OIDC verification
* Introduction of a new endpoint to Verify a Credential directly using the API
# Supported standards
URL: /docs/resources/standards/supported
This page outlines the currently-implemented support of MATTR products and services for various open
standards. If there is a standard (listed here or not) for which you would like further
clarification with respect to product support, please [contact us](http://mattr.global/contact-us).
## Standards authorities [#standards-authorities]
The following is a list of globally-recognized standards development organizations (SDO's) where we
help develop and participate in standardization efforts and implement standards:
* [Decentralized Identity Foundation (DIF)](https://identity.foundation/)
* [OpenID Foundation (OIDF)](https://openid.net/foundation/)
* [World Wide Web Consortium (W3C)](https://www.w3.org/)
* [Internet Engineering Task Force (IETF)](https://www.ietf.org/)
* [International Organization for Standardization (ISO)](https://www.iso.org/home.html)
## Supported standards status [#supported-standards-status]
With respect to each standard, the status indicates the current and likely continued future
conformance of relevant MATTR products and services to the particular version of the standard
indicated. The status is influenced by the product roadmap, future iterations of the standard
itself, the changing standards landscape, and other such factors.
Support statuses are categorized as either:
* **Stable** - MATTR products and services conform to the standard and conformance is expected to
continue into the foreseeable future. Deprecation of support for the standard and/or adoption of a
new version is unlikely over any reasonable time horizon.
* **Provisionary** - MATTR products and services conform to the standard however deprecation of
support for the standard and/or adoption of a new version is possible within the foreseeable
future.
* **Deprecated** - MATTR products and services previously conformed to the standard, the support for
which has now ceased.
### Core standards supported [#core-standards-supported]
| Standards organization | Standard | Version | Status |
| :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------- | :----------- |
| W3C | [Decentralized Identifiers (DIDs)](https://www.w3.org/TR/did-core/ "Decentralized Identifiers (DIDs)") | 1.0 | Stable |
| W3C | [Verifiable Credentials Data Model](https://www.w3.org/TR/vc-data-model/ "Verifiable Credentials Data Model") | 1.1 | Stable |
| W3C | [JSON-LD](https://www.w3.org/TR/json-ld/ "JSON-LD") | 1.1 | Stable |
| W3C CCG | [Revocation List 2020](https://w3c-ccg.github.io/vc-status-rl-2020/ "Revocation List 2020") | Draft Community Group Report 20 April 2021 | Provisionary |
| W3C CCG | [Verifiable Presentation Request Specification](https://w3c-ccg.github.io/vp-request-spec/ "Verifiable Presentation Request Specification") | 0.7 Draft Community Group Report 26 September 2022 | Provisionary |
| W3C/WICG | [Digital Credentials API](https://github.com/w3c-fedid/digital-credentials) | WD | Provisionary |
| DIF | [DIDComm Messaging](https://identity.foundation/didcomm-messaging/spec/ "DIDComm Messaging") | 1.0 | Provisionary |
| DIF | [Well Known DID Configuration](https://identity.foundation/.well-known/resources/did-configuration/ "Well Known DID Configuration") | DIF Working Group Approved Draft | Stable |
| IETF | [JSON Web Encryption (JWE)](https://datatracker.ietf.org/doc/rfc7516/) | 1.0 | Stable |
| IETF | [JSON Web Key (JWK)](https://datatracker.ietf.org/doc/rfc7517/) | 1.0 | Stable |
| IETF | [JSON Web Signature (JWS)](https://datatracker.ietf.org/doc/rfc7515/) | 1.0 | Stable |
| IETF | [HTTP Signatures](https://datatracker.ietf.org/doc/draft-ietf-httpbis-message-signatures/) | Proposed standard | Stable |
| IETF | [Token Status List](https://drafts.oauth.net/draft-ietf-oauth-status-list/draft-ietf-oauth-status-list.html) | Draft 14 | Stable |
| OIDF | [OpenID Connect](https://openid.net/connect/ "OpenID Connect") | 1.0 | Stable |
| OIDF | [OpenID4VCI](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html) | 1.0 | Stable |
| OIDF | [OpenID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html) | ID-2 | Provisionary |
| ISO/IEC | [18013-5 - Mobile Driving License (mDL)](https://www.iso.org/standard/69084.html) | 2021 | Stable |
| ISO/IEC | [18013-7 - Mobile Driving License (mDL) add-on functions](https://www.iso.org/standard/91154.html) | 2024 | Stable |
| ISO/IEC | [23220-4 - Identity management via mobile devices - protocols and services for operational phase](https://www.iso.org/standard/86785.html) | Final DTS | Provisionary |
| ISO/IEC | [23220-3 - Identity management via mobile devices - protocols and services for issuing phase](https://www.iso.org/standard/86783.html) | WD14 | Provisionary |
### DID methods supported [#did-methods-supported]
| Standards organization | Standard | Version | Status |
| :--------------------- | :--------------------------------------------------- | :------------------------------------- | :----------- |
| W3C | [DID Key](https://w3c-ccg.github.io/did-key-spec/) | 0.7 Unofficial Draft 02 September 2022 | Provisionary |
| W3C | [DID Web](https://w3c-ccg.github.io/did-method-web/) | 30 January 2023 | Provisionary |
### Cryptographic suites supported [#cryptographic-suites-supported]
| Standards organization | Standard | Version | Status |
| :--------------------- | :-------------------------------------------------------------------------------------------- | :---------------------------------------- | :----------- |
| IETF | [BBS Signatures Suite 2022](https://datatracker.ietf.org/doc/draft-irtf-cfrg-bbs-signatures/) | Proposed standard | Provisionary |
| W3C | BBS+ Signatures 2020 | Draft Community Group Report 16 Jan 2023 | Deprecated |
| W3C | [Ed25519 Signature 2018](https://w3c-ccg.github.io/lds-ed25519-2018/) | Draft Community Group Report 23 July 2021 | Provisionary |
# Upcoming standards
URL: /docs/resources/standards/upcoming
This page outlines the open standards that are actively being followed by MATTR with a view to
potential placement on the product roadmap and eventual product support. If there is a standard
(listed here or not) for which you would like further clarification with respect to product support,
please [contact us](http://mattr.global/contact-us).
## Standards authorities [#standards-authorities]
The following is a list of globally-recognized standards development organizations (SDO's) where we
help develop and participate in standardization efforts and implement standards:
* [Decentralized Identity Foundation (DIF)](https://identity.foundation/)
* [OpenID Foundation (OIDF)](https://openid.net/foundation/)
* [World Wide Web Consortium (W3C)](https://www.w3.org/)
* [Internet Engineering Task Force (IETF)](https://www.ietf.org/)
* [International Organization for Standardization (ISO)](https://www.iso.org/home.html)
## Standards [#standards]
| Standards organization | Standard | Version |
| :--------------------- | :--------------------------------------------------------------------------------------- | :------ |
| IETF | [JSON Web Proof (JWP)](https://datatracker.ietf.org/doc/draft-ietf-jose-json-web-proof/) | -04 |
# Terms and Conditions
URL: /docs/resources/terms
The terms governing your use of MATTR products and services.
Our commitments regarding the availability and performance of our services.
How we collect, use, and protect your personal information.
Details on how we handle and process data on your behalf.
A list of third-party sub-processors we engage to assist in providing our services.
The terms governing your use of our website.
The terms governing your use of our software development kits (SDKs).
The terms governing your use of our MATTR GO whitelabel applications.
# How to configure holder certificates
URL: /docs/holding/certificates/guide
Holder applications use [Wallet Attestation](/docs/holding/credential-claiming-guides/wallet-attestation)
to prove their authenticity to credential issuers. This attestation is verified using a
[chain of trust](/docs/holding/certificates/overview) model, where wallet attestation JWTs are linked
to the holder application operator via a series of certificates. MATTR VII supports both managed and
unmanaged (external) holder certificates, allowing you to choose how you manage your certificate
infrastructure.
* With **managed** holder certificates, you create a Holder root CA certificate and MATTR VII
manages the rest. Wallet attestation signers (and their certificates) are automatically
provisioned when needed to sign wallet attestation JWTs.
* With **unmanaged** holder certificates, you manage the entire lifecycle. You generate the Holder
root CA certificate, create wallet attestation signers, use the returned CSR to obtain signed
certificates from your root CA, and upload them to MATTR VII. See
[unmanaged certificates](/docs/holding/certificates/overview#unmanaged-external-certificates) for
more details.
## Creating a Holder root CA certificate [#creating-a-holder-root-ca-certificate]
### Create a managed Holder root CA [#create-a-managed-holder-root-ca]
Make a request of the following structure to
[create a managed Holder root CA](/docs/api-reference/platform/holder-root-ca-certificates/createHolderCaCertificate):
```http filename:"Request"
POST /v1/holder/certificates/ca
```
```json filename:"Request body"
{
"commonName": "Example Holder root CA",
"country": "NZ"
}
```
* `commonName` : This *optional* parameter indicates the common name of the Holder root CA certificate. When specified, the value must be a valid `PrintableString` and cannot be an empty string. If not provided, the value defaults to `{tenantDomain} Wallet Attestation`.
* `country` : This *optional* parameter indicates the holder country. When specified, the value must
be a valid country code as per
[ISO 3166-1](https://www.iso.org/iso-3166-country-codes.html).
The response will include an `id` property, which is a unique identifier for the Holder root CA. This identifier is used in subsequent operations to reference this Holder root CA.
The Holder root CA is always created as **inactive**. You will activate it in the next step.
### Activate the Holder root CA [#activate-the-holder-root-ca]
Make a request of the following structure to
[update the Holder root CA](/docs/api-reference/platform/holder-root-ca-certificates/updateHolderCaCertificate)
and activate it:
```http filename:"Request"
PUT /v1/holder/certificates/ca/{certificateId}
```
* `certificateId` : Replace with the `id` value obtained when you created the Holder root CA.
```json filename:"Request body"
{
"active": true
}
```
Setting `active: true` deactivates any previously active Holder root CA for the tenant (only one can
be active at a time).
Once a managed Holder root CA is activated, MATTR VII automatically provisions wallet attestation
signers on demand when the first wallet attestation request is made. No additional configuration is
required.
### Generate a self-signed root certificate (Holder root CA) [#generate-a-self-signed-root-certificate-holder-root-ca]
Use your preferred cryptographic library or tool to generate a self-signed root certificate (Holder
root CA). This certificate will later be used to sign wallet attestation signer certificates.
When using unmanaged (external) certificates, you assume full responsibility for the secure
management of the root certificate and all subordinate certificates. This includes ensuring the
protection, proper issuance, and timely revocation of certificates under the uploaded root, as
MATTR VII does not manage or monitor these certificates on your behalf.
### Register the external Holder root CA certificate with MATTR VII [#register-the-external-holder-root-ca-certificate-with-mattr-vii]
Make a request of the following structure to
[create an unmanaged Holder root CA](/docs/api-reference/platform/holder-root-ca-certificates/createHolderCaCertificate):
```http filename:"Request"
POST /v1/holder/certificates/ca
```
```json filename:"Request body"
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICAzCCAaqgAwIBAgIJAKFOoPvy+rfbMAoGCCqGSM49BAMCMCUxCzAJBgNVBAYT\r\nAk5aMRYwFAYDVQQDDA1NQVRUUiBFeGFtcGxlMB4XDTI2MDUxNDAwMjEzNVoXDTM2\r\nMDUxMTAwMjEzNVowJTELMAkGA1UEBhMCTloxFjAUBgNVBAMMDU1BVFRSIEV4YW1w\r\nbGUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATTSjjTuOouwLNObXEwm0TNNWu5\r\nQfuWW1aMaIz/SwzKfHEP8A4fAgiEYF5SfO5Fy+cQ8m159e3go7yAzmyE1zgIo4HC\r\nMIG/MBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQW\r\nBBQ8paHD6yC0HheUE53YCRx1fR/8JzB6BgNVHR8EczBxMG+gbaBrhmlodHRwczov\r\nL2V4YW1wbGUuY29tL3YyL2NyZWRlbnRpYWxzL2hvbGRlci93YWxsZXQtYXR0ZXN0\r\nYXRpb24vY2FzLzJlODljMTU2LTMxZDUtNDc4My1iZDU5LTkwNTViNWY4ZTdkMi9j\r\ncmwwCgYIKoZIzj0EAwIDRwAwRAIgJkH+qkQ4Rz//MBU61u3fDZOrHYejR2FIK1Lv\r\nIEiNRDACIHePIX2AEK0Ls6wIxgyJt4hdj64QSSzH1hJzMPGGFyM9\r\n-----END CERTIFICATE-----\r\n"
}
```
* `certificatePem` : This required parameter contains the PEM-encoded Holder root CA certificate.
The certificate must be valid and not expired.
The response will include an `id` property, which is a unique identifier for the unmanaged Holder
root CA. This identifier is used in subsequent operations to reference this Holder root CA.
### Create a wallet attestation signer [#create-a-wallet-attestation-signer]
Make a request of the following structure to
[create a wallet attestation signer](/docs/api-reference/platform/wallet-attestation-signers/createWalletAttestationSigner)
that references the unmanaged Holder root CA:
```http filename:"Request"
POST /v1/holder/certificates/wallet-attestation-signers
```
```json filename:"Request body"
{
"caId": "080c670a-2e90-4023-b79f-b706e55e9bc6"
}
```
* `caId` : Replace with the `id` value obtained when you created the unmanaged Holder root CA in the
previous step. Attempts to provide a managed Holder root CA identifier for manual wallet
attestation signer creation will result in an error.
The response will include two properties which you will use later in this guide:
* `id` : The unique identifier for the wallet attestation signer. This identifier is used in
subsequent operations to reference this signer.
* `csrPem` : The X.509 Certificate Signing Request (CSR) in PEM format. You will use this CSR to
generate a valid wallet attestation signer certificate in the next step.
### Generate and sign the wallet attestation signer certificate [#generate-and-sign-the-wallet-attestation-signer-certificate]
Use your preferred cryptographic library or tool to generate and sign a wallet attestation signer
certificate using the CSR provided in the response from the previous step. The certificate must be
signed by your Holder root CA.
### Associate the certificate with the wallet attestation signer [#associate-the-certificate-with-the-wallet-attestation-signer]
Make a request of the following structure to
[update the wallet attestation signer](/docs/api-reference/platform/wallet-attestation-signers/updateWalletAttestationSigner)
to upload the signed certificate and activate the signer:
```http filename:"Request"
PUT /v1/holder/certificates/wallet-attestation-signers/{certificateId}
```
* `certificateId` : Replace with the `id` value obtained when you created the wallet attestation
signer in the previous step.
```json filename:"Request body"
{
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICRjCCAe2gAwIBAgIJAMaBeZ37qSVQMAoGCCqGSM49BAMCMCUxCzAJBgNVBAYT\r\nAk5aMRYwFAYDVQQDDA1NQVRUUiBFeGFtcGxlMB4XDTI2MDUxNDAwMjEzNVoXDTI3\r\nMDUxNDAwMjEzNVowTTELMAkGA1UEBhMCTloxPjA8BgNVBAMMNU1BVFRSIEV4YW1w\r\nbGUgV2FsbGV0IEF0dGVzdGF0aW9uIFNpZ25lciAxNzc4NzE2ODAwMDAwMFkwEwYH\r\nKoZIzj0CAQYIKoZIzj0DAQcDQgAE2bX0ZXV2DyFj3d1Va98s4PiBMN48ZFCf2dQZ\r\nl6Y/lIZfQQjQcIEWi+dKAsYyaqeUi81yxzvXfmRogVh9nweQoaOB3TCB2jAMBgNV\r\nHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDAdBgNVHQ4EFgQUQ3gaPaANZ83EHYNE\r\nrgjFP8z3VtowHwYDVR0jBBgwFoAUPKWhw+sgtB4XlBOd2AkcdX0f/CcwegYDVR0f\r\nBHMwcTBvoG2ga4ZpaHR0cHM6Ly9leGFtcGxlLmNvbS92Mi9jcmVkZW50aWFscy9o\r\nb2xkZXIvd2FsbGV0LWF0dGVzdGF0aW9uL2Nhcy8yZTg5YzE1Ni0zMWQ1LTQ3ODMt\r\nYmQ1OS05MDU1YjVmOGU3ZDIvY3JsMAoGCCqGSM49BAMCA0cAMEQCICdeu9XvaCqW\r\nYwkST+wYtrm4awOt+mRQwqrMMVjAQj1BAiBgdrg0JeXyofGrZvb34TEM/gQDi/HV\r\nHCqUhEUm9T92pw==\r\n-----END CERTIFICATE-----\r\n"
}
```
* `active` : Set to `true` to activate the signer. Can only be set to `true` when a
`certificatePem` is provided. Only active wallet attestation signers can be used to sign wallet
attestation JWTs.
* `certificatePem` : The PEM-encoded certificate signed by your Holder root CA. This field is
immutable after the first upload — subsequent PUT requests may only toggle `active`.
### Activate the Holder root CA [#activate-the-holder-root-ca-1]
Make a request of the following structure to
[update the Holder root CA](/docs/api-reference/platform/holder-root-ca-certificates/updateHolderCaCertificate)
and activate it:
```http filename:"Request"
PUT /v1/holder/certificates/ca/{certificateId}
```
* `certificateId` : Replace with the `id` value obtained when you registered the unmanaged Holder
root CA.
```json filename:"Request body"
{
"active": true
}
```
### Wallet Attestation is ready [#wallet-attestation-is-ready]
Once the Holder root CA and wallet attestation signer are activated, they are used to sign wallet
attestation JWTs. When a holder application with
[SDK Tethering](/docs/holding/sdk-operations/sdk-tethering) configured requests a Wallet Attestation
token, the tethered MATTR VII automatically selects a valid and active wallet attestation signer to sign the JWT.
If there is no valid and active wallet attestation signer, MATTR VII will return an error. Unlike
the managed flow, MATTR VII does not automatically create new wallet attestation signers in the
unmanaged flow, and you are responsible for manually creating and uploading them as needed.
# Overview
URL: /docs/holding/certificates/overview
## Chain of trust [#chain-of-trust]
When holder applications request [wallet attestation](/docs/holding/credential-claiming-guides/wallet-attestation) tokens from their tethered MATTR VII tenant, issuers need a way to
confirm the wallet's identity and decide whether to trust these tokens. This is
accomplished using a [chain of trust](/docs/concepts/chain-of-trust), a hierarchy of certificates
that proves the wallet's authenticity.
The following diagram depicts how MATTR implements the chain of trust model when signing
wallet attestation tokens:
1. **Holder root CA certificate**: The root of the trust chain. A self-signed X.509 certificate that
identifies the holder application operator. It is used to sign wallet attestation signer
certificates.
2. **Wallet Attestation Signer Certificate (WASC)**: An end-entity X.509 certificate signed by the Holder
root CA. It is used to sign wallet attestation JWTs that are presented to credential issuers
as [Wallet Attestation](/docs/holding/credential-claiming-guides/wallet-attestation) proofs during credential claiming
flows.
3. **Wallet attestation JWT**: The end-entity of the chain. A signed JWT presented to the credential
issuer that attests the holder application's identity and authenticity.
## Managed vs. unmanaged certificates [#managed-vs-unmanaged-certificates]
MATTR VII supports both managed and unmanaged (external) Holder root CA certificates, giving you
flexibility in how you manage your certificate infrastructure.
### Managed certificates [#managed-certificates]
With managed certificates, MATTR VII generates and manages the Holder root CA certificate and its
private key on your behalf. Wallet attestation signers are automatically provisioned on demand when
the first wallet attestation request is made, and you do not need to create them explicitly.
This is the simplest approach and is recommended for most use cases.
### Unmanaged (external) certificates [#unmanaged-external-certificates]
With unmanaged certificates, you supply your own externally-managed Holder root CA in PEM format.
You are then responsible for:
* Generating and protecting the Holder root CA certificate and its private key.
* Creating wallet attestation signers and using the returned Certificate Signing Request (CSR) to
obtain signed certificates from your root CA.
* Uploading the signed certificates to MATTR VII.
* Managing certificate rotation and lifecycle.
This approach gives you full control over the certificate chain but requires more operational
overhead. See the
[external certificates](/docs/concepts/chain-of-trust#external-certificates) documentation for more
details on the unmanaged certificate model.
## Certificate limits [#certificate-limits]
* A maximum of **three** Holder root CA certificates can be created per tenant.
* Only **one** Holder root CA can be active at a time. Activating a new root automatically
deactivates the previously active one.
* A maximum of **five** wallet attestation signers can be created per Holder root CA certificate.
## Certificate requirements [#certificate-requirements]
The following lists depict the requirements for certificates used in the holder certificate chain. Some
requirements are common across all certificates, while others are specific to the type of
certificate (Holder root CA, wallet attestation signer certificate).
* When using **managed holder certificates**, MATTR VII **automatically** ensures that all
certificates meet these requirements.
* When using **unmanaged holder certificates**, it is the responsibility of the **holder application
operator** to ensure compliance.
### Common certificate requirements [#common-certificate-requirements]
* Certificate format & basic attributes:
* PEM format must contain a valid X.509 certificate.
* Version must be v3.
* `Issuer` field must be present and valid.
* Issuer Alternative Name must be present and contain a valid email address or URI.
* Serial Number:
* Must be present.
* Must contain 1-20 digits (**Best practice**: Use a positive, non-sequential value).
* Subject attributes:
* Subject field must be present:
* Country (C): must be present and be a valid
[ISO 3166-1 alpha-2 code](https://www.iso.org/glossary-for-iso-3166.html).
* Common Name (CN): must be present and non-empty.
* Public Key requirements:
* Subject Public Key must be present.
* Extensions:
* Certificate must include extensions.
* Duplicate extensions are not allowed (No more than one extension with the same `extnID`).
* Mandatory extensions:
* Subject Key Identifier: Must be present and non-empty.
* Key Usage:
* Must be present.
* Must match the intended use of the certificate (e.g. Holder root CA, wallet
attestation signer).
* Validity period:
* `NotAfter` must be after `NotBefore`.
* `NotAfter` cannot be in the past (expired certificates are invalid).
* Future dated certificates are valid (e.g. `notBefore` can be in the future).
* Certificate Revocation List (CRL):
* If a CRL is provided, it must be valid and signed by the Holder root CA.
* The CRL must be accessible via a valid URI.
### Holder root CA specific requirements [#holder-root-ca-specific-requirements]
* Must include the `keyCertSign` and `cRLSign` key usages.
* Basic constraints must be present and `CA` must be set to `TRUE`.
* Issuer Alternative Name must be present and contain a valid email address or URI.
* Signature must be self-signed and verifiable.
* Public key must use one of the supported public key algorithms and curves:
* ECDSA curves: `P-256`, `P-384`, `P-521`, `brainpoolP256r1`, `brainpoolP320r1`,
`brainpoolP384r1`, `brainpoolP512r1`
* EdDSA key types: `Ed25519`, `Ed448`
### Wallet attestation signer certificate specific requirements [#wallet-attestation-signer-certificate-specific-requirements]
* Must be signed by a valid Holder root CA.
* Common name must differ from parent/root Holder root CA.
* `Issuer` field must be present and must match the exact binary value of the Holder root CA
certificate subject.
* Must include the `digitalSignature` key usage exclusively.
* Signature must be verifiable against the Holder root CA.
* Authority Key Identifier must be present and match the Holder root CA's Subject Key Identifier.
* Must not exceed parent Holder root CA's validity period (i.e. `notBefore` and `notAfter` must be
within the Holder root CA's validity period).
* Public key must match the CSR provided during wallet attestation signer creation.
# How to configure authentication requirements for credentials using Device Key Authentication
URL: /docs/holding/credential-claiming-guides/device-key-authentication-guide
## Overview [#overview]
This guide demonstrates how to use **Device Key Authentication** to control what user authentication methods (such as face authentication, fingerprint authentication, or the device passcode) are required when claiming and presenting credentials in your wallet applications.
When the SDK is initialized, the `UserAuthConfiguration` structure defines the default authentication behavior for the wallet application, including settings like when authentication is required and the timeout duration.
Device Key Authentication gives you fine-grained control over the security posture of specific credentials. By specifying a per-credential authentication policy, you can require stronger methods for sensitive credentials while allowing more convenient access for less sensitive ones.
When you specify a `DeviceKeyAuthenticationPolicy` during credential claiming or presentation, it overrides the general `UserAuthConfiguration` settings for that specific operation. This means that the authentication requirements defined in the `DeviceKeyAuthenticationPolicy` take precedence over the broader wallet-level settings. Conversely, if no `DeviceKeyAuthenticationPolicy` is specified, the SDK falls back to the `UserAuthConfiguration` settings for authentication behavior.
Each policy leverages whatever user authentication modalities are currently configured at the operating system level (e.g., enrolled face authentication / fingerprints and an active device passcode). The wallet does not manage biometric enrollment. It relies on the device’s Secure Enclave / Keystore settings. By choosing the appropriate policy, you align credential protection directly with the user’s device-level security configuration.
## Prerequisites [#prerequisites]
This guide builds on the [Credential Claiming tutorial](/docs/holding/credential-claiming-tutorial). It is recommended to complete that tutorial first, then return here to learn how to configure device key authentication policies.
## Understanding Device Key Authentication [#understanding-device-key-authentication]
### What is Device Key Authentication? [#what-is-device-key-authentication]
Device Key Authentication is a feature that allows you to specify which type of user authentication is required to access the cryptographic keys protecting each credential. These keys are generated on the user's device and are used for:
1. **Secure credential claiming**: Authenticate the user when a new credential is being claimed and stored in the wallet.
2. **Secure credential usage**: Authenticate the user when an existing credential is accessed, either to view it in the wallet or to present it to a verifier (in-person or remotely).
By configuring a device key authentication policy, you can define the level of security required for each credential based on its sensitivity and use case.
Device Key Authentication enables you to:
* **Match security to sensitivity**: Require stronger authentication (like biometrics) for high-value credentials such as government IDs, while allowing passcode-only access for lower-risk credentials like membership cards.
* **Improve user experience**: Balance security with convenience by allowing users to choose appropriate authentication methods for different credential types.
* **Maintain consistency**: Ensure the same authentication method used during credential claiming is required during presentation, providing a predictable and secure user experience.
* **Meet compliance requirements**: Satisfy regulatory or organizational requirements that mandate specific authentication methods for certain credential types.
### How it works [#how-it-works]
When you configure a device key authentication policy for a credential:
1. **During claiming**: The user must authenticate using the specified method before the credential can be stored in their wallet.
2. **During access**: The same authentication method is required before the credential can be accessed, either to view it in the wallet or to present it to a verifier (in-person or remotely).
The authentication policy is bound to the device key that protects the credential, ensuring consistent security throughout the credential's lifecycle.
## Available authentication options [#available-authentication-options]
The Holder SDKs provide several authentication policy options:
The iOS Holder SDK authentication policy options map directly to Apple's [SecAccessControlCreateFlags](https://developer.apple.com/documentation/security/secaccesscontrolcreateflags):
| UserAuthenticationType | Biometry | Credential valid after biometry changed | Lockscreen | Security level |
| ---------------------- | :------: | :-------------------------------------: | :--------: | :------------: |
| `biometryCurrentSet` | ✅ | ❌ | ❌ | Highest |
| `biometryAny` | ✅ | ✅ | ❌ | High |
| `deviceCredential` | ❌ | ✅ | ✅ | Medium |
| `userPresence` | ✅ | ✅ | ✅ | Lowest |
* `biometryAny`: Adding or removing biometric data does not invalidate keys (e.g., user adds a new TouchID enrollment or deletes an existing FaceID enrollment after claiming the credential. Existing device keys remain usable and no re-provisioning is required). Maps to Apple's `SecAccessControlCreateFlags.biometryAny`.
* `biometryCurrentSet`: Adding or removing biometric data invalidates the key (e.g., user adds a new TouchID enrollment or deletes an existing FaceID enrollment after claiming the credential. The underlying device key becomes unusable and the credential is invalid. The user must re-claim the credential from the issuer). Maps to Apple's `SecAccessControlCreateFlags.biometryCurrentSet`.
* `deviceCredential`: Key remains valid if passcode changes but becomes invalid if passcode is removed. Maps to Apple's `SecAccessControlCreateFlags.devicePasscode`.
* `userPresence`: Requires user presence (biometry or passcode) each access. Adding/removing biometry does not invalidate keys. Maps to Apple's `SecAccessControlCreateFlags.userPresence`.
If a credential’s authentication policy differs from the policy set at SDK initialization, the user may see two authentication prompts. One prompt enforces the SDK-level policy; the other enforces the credential’s policy (each tied to a different key). This can occur when:
* The user attempts to retrieve or access a credential shortly after initialization.
* At least five minutes have passed since initialization and the user then attempts to retrieve or access a credential.
| UserAuthenticationType | Biometry | Credential valid after biometry changed | Lockscreen | Security level |
| ---------------------- | :------: | :-------------------------------------: | :--------: | :------------: |
| `BiometryCurrentSet` | ✅ | ❌ | ❌ | Highest |
| `BiometryAny` | ✅ | ✅ | ❌ | High |
| `DeviceCredential` | ❌ | ✅ | ✅ | Medium |
| `UserPresence` | ✅ | ✅ | ✅ | Lowest |
* `BiometryAny`: Adding or removing biometric data does not invalidate keys (e.g., user adds a new fingerprint or deletes an existing face authentication enrollment after claiming the credential. Existing device keys remain usable and no re-provisioning is required).
* `BiometryCurrentSet`: Adding or removing biometric data invalidates the key (e.g., user adds a new fingerprint or deletes an existing face authentication enrollment after claiming the credential. The underlying device key becomes unusable and the credential is invalid. The user must re-claim the credential from the issuer).
* `DeviceCredential`: Key remains valid if passcode changes but becomes invalid if passcode is removed.
* `UserPresence`: Requires user presence (biometry or passcode) each access. Adding/removing biometry does not invalidate keys.
The React Native Holder SDK uses the `DeviceKeyAuthenticationType` enum for authentication policy options:
| DeviceKeyAuthenticationType | Biometry | Credential valid after biometry changed | Lockscreen | Security level |
| --------------------------- | :------: | :-------------------------------------: | :--------: | :------------: |
| `BiometryCurrentSet` | ✅ | ❌ | ❌ | Highest |
| `BiometryAny` | ✅ | ✅ | ❌ | High |
| `DeviceCredential` | ❌ | ✅ | ✅ | Medium |
| `UserPresence` | ✅ | ✅ | ✅ | Lowest |
* `BiometryAny`: Adding or removing biometric data does not invalidate keys (e.g., user adds a new fingerprint or deletes an existing face authentication enrollment after claiming the credential. Existing device keys remain usable and no re-provisioning is required).
* `BiometryCurrentSet`: Adding or removing biometric data invalidates the key (e.g., user adds a new fingerprint or deletes an existing face authentication enrollment after claiming the credential. The underlying device key becomes unusable and the credential is invalid. The user must re-claim the credential from the issuer).
* `DeviceCredential`: As long as the user has unlocked their phone, they have access to the key. Any authentication method is allowed.
* `UserPresence`: Requires user presence (biometry or passcode) on each access to the key. Any authentication method is allowed. Adding/removing biometry does not invalidate keys.
On iOS devices, if a credential’s authentication policy differs from the policy set at SDK initialization, the user may see two authentication prompts. One prompt enforces the SDK-level policy; the other enforces the credential’s policy (each tied to a different key). This can occur when:
* The user attempts to retrieve or access a credential shortly after initialization.
* At least five minutes have passed since initialization and the user then attempts to retrieve or access a credential.
## Configuring Device Key Authentication [#configuring-device-key-authentication]
To configure device key authentication, you need to specify a `DeviceKeyAuthenticationPolicy` when calling the `retrieveCredentials` method during the credential claiming flow.
### Step 1: Create a DeviceKeyAuthenticationPolicy [#step-1-create-a-devicekeyauthenticationpolicy]
First, create a `DeviceKeyAuthenticationPolicy` object that defines the authentication requirements:
```swift title="ContentView"
let deviceKeyAuthPolicy = DeviceKeyAuthenticationPolicy(
type: .biometryAny
)
```
The `DeviceKeyAuthenticationPolicy` takes a single parameter:
* `type`: The type of user authentication required. Choose from one of the options detailed [above](#available-authentication-options).
First, create a `DeviceKeyAuthenticationPolicy` object that defines the authentication requirements:
```kotlin title="MainActivity.kt"
val deviceKeyAuthPolicy = DeviceKeyAuthenticationPolicy(
type = UserAuthenticationType.BiometryAny
)
```
The `DeviceKeyAuthenticationPolicy` takes a single parameter:
* `userAuthenticationType`: The type of user authentication required. Choose from one of the options detailed [above](#available-authentication-options).
First, import the required types and create a `DeviceKeyAuthenticationPolicy` object that defines the authentication requirements:
```tsx title="App.tsx"
import {
DeviceKeyAuthenticationType,
} from "@mattrglobal/mobile-credential-holder-react-native";
const authenticationPolicy = {
type: DeviceKeyAuthenticationType.BiometryAny,
};
```
The `DeviceKeyAuthenticationPolicy` takes a single parameter:
* `type`: The type of user authentication required. Choose from one of the `DeviceKeyAuthenticationType` options detailed [above](#available-authentication-options).
### Step 2: Pass the policy to retrieveCredentials [#step-2-pass-the-policy-to-retrievecredentials]
Update your `retrieveCredential` function from the [Credential Claiming tutorial](/docs/holding/credential-claiming-tutorial#step-4-retrieve-the-credential) to include the `deviceKeyAuthenticationPolicy` parameter:
```swift title="ContentView"
func retrieveCredential(transactionCode: String?) {
Task {
do {
// Create the device key authentication policy
let deviceKeyAuthPolicy = DeviceKeyAuthenticationPolicy(
type: .biometryAny
)
let retrievedCredentialResults = try await mobileCredentialHolder.retrieveCredentials(
credentialOffer: discoveredCredentialOfferURL,
clientId: Constants.clientId,
transactionCode: transactionCode,
deviceKeyAuthenticationPolicy: deviceKeyAuthPolicy // [!code focus]
)
Task {
var credentials: [MobileCredential] = []
for result in retrievedCredentialResults {
switch result {
case .success(_, let credentialId):
if let credential = try? await mobileCredentialHolder.getCredential(credentialId: credentialId) {
credentials.append(credential)
}
case .failure(let docType, let error):
print("Failed to retrieve \(docType): \(error)")
}
}
self.retrievedCredentials = credentials
navigationPath = NavigationPath()
navigationPath.append(NavigationState.retrievedCredentials)
}
} catch {
print(error.localizedDescription)
}
}
}
```
Update your `onRetrieveCredentials` function from the [Credential Claiming tutorial](/docs/holding/credential-claiming-tutorial#step-4-retrieve-the-credential) to include the `DeviceKeyAuthenticationPolicy` parameter:
```kotlin title="MainActivity.kt"
private fun onRetrieveCredentials(
coroutineScope: CoroutineScope,
activity: Activity,
navController: NavController,
transactionCode: String
) {
coroutineScope.launch {
try {
val deviceKeyAuthPolicy = DeviceKeyPolicy(
DeviceKeyAuthenticationPolicy(type = UserAuthenticationType.BiometryAny)
)
val mdocHolder = MobileCredentialHolder.getInstance()
val retrieveCredentialResults = mdocHolder.retrieveCredentials(
activity,
SharedData.scannedOffer!!,
clientId = "android-mobile-credential-tutorial-holder-app",
transactionCode = transactionCode,
deviceKeyAuthPolicy
)
SharedData.retrievedCredentials = retrieveCredentialResults.mapNotNull { result ->
when (result) {
is RetrieveCredentialResult.Success -> try {
mdocHolder.getCredential(result.credentialId, fetchUpdatedStatusList = false)
} catch (e: Exception) {
val msg = "Failed to get credential from storage"
Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show()
null
}
is RetrieveCredentialResult.Failure -> {
val msg = "Failed to retrieve ${result.docType}: ${result.error}"
Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show()
null
}
}
}
navController.navigate("retrievedCredential")
SharedData.discoveredCredentialOffer = null
} catch (e: Exception) {
Toast.makeText(activity, "Failed to retrieve credentials", Toast.LENGTH_SHORT).show()
}
}
}
```
Update your `retrieveCredentials` call from the [Credential Claiming tutorial](/docs/holding/credential-claiming-tutorial#step-4-retrieve-the-credential) to include the `authenticationPolicy` parameter:
```tsx title="App.tsx"
import {
retrieveCredentials,
getCredential,
DeviceKeyAuthenticationType,
} from "@mattrglobal/mobile-credential-holder-react-native";
const onRetrieveCredentials = async (
credentialOfferUrl: string,
transactionCode?: string,
) => {
// Create the device key authentication policy
const authenticationPolicy = {
type: DeviceKeyAuthenticationType.BiometryAny,
};
const retrieveResult = await retrieveCredentials({
credentialOffer: credentialOfferUrl,
clientId: "react-native-mobile-credential-tutorial-holder-app",
transactionCode,
authenticationPolicy, // [!code focus]
});
if (retrieveResult.isErr()) {
// Handle error
return;
}
const results = retrieveResult.value;
for (const result of results) {
if (result.isSuccess) {
const credentialResult = await getCredential({
credentialId: result.credentialId,
fetchUpdatedStatusList: false,
});
// Process credential
} else {
// Handle result.error for result.docType
}
}
};
```
## Understanding the complete flow [#understanding-the-complete-flow]
When you call `retrieveCredentials` with a `deviceKeyAuthenticationPolicy`:
1. The SDK generates a device key with the specified authentication requirements.
2. The user is prompted to authenticate using the specified method (e.g., Face ID or fingerprint).
3. The authentication requirement is bound to the access control of the generated device key, and that key is cryptographically bound to the credential.
4. Once authenticated, the credential is claimed and securely stored.
5. Future access to this credential (during presentation workflows) will require the same authentication method.
## Verifying the authentication policy [#verifying-the-authentication-policy]
You can inspect the authentication policy associated with a credential by accessing its metadata:
```swift title="Inspecting authentication policy"
if let credential = try? await mobileCredentialHolder.getCredential(credentialId: credentialId) {
if let authInfo = credential.metadata.deviceKeyAuthenticationInfo {
print("User authentication type: \(authInfo.type?.rawValue ?? "not set")")
}
}
```
The `deviceKeyAuthenticationInfo` property returns a `DeviceKeyAuthenticationInfo` object containing:
* `type`: The authentication type configured for this credential's device key.
```kotlin title="Inspecting authentication policy"
val credentialMetadata = mobileCredentialHolder.getCredentials().first { it.id == credentialId }
credentialMetadata.deviceKeyPolicy?.authentication?.type?.let {
println("User authentication type: $it")
}
```
The `deviceKeyPolicy` property returns a `DeviceKeyPolicyInfo` object with `authentication` property, containing:
* `type`: The authentication type configured for this credential's device key.
* `timeoutSeconds`: Timeout for user authentication.
```tsx title="Inspecting authentication policy"
import {
getCredential,
} from "@mattrglobal/mobile-credential-holder-react-native";
const credentialResult = await getCredential({
credentialId,
fetchUpdatedStatusList: false,
});
if (credentialResult.isOk()) {
const credential = credentialResult.value;
const authInfo = credential.metadata.deviceKeyAuthenticationInfo;
if (authInfo) {
console.log(`User authentication type: ${authInfo.type}`);
}
}
```
The `deviceKeyAuthenticationInfo` property returns a `DeviceKeyAuthenticationInfo` object containing:
* `type`: The authentication type configured for this credential's device key.
## Testing Device Key Authentication [#testing-device-key-authentication]
To test the device key authentication feature:
1. **Complete the credential claiming flow** with a configured `deviceKeyAuthenticationPolicy` as described above.
2. **Observe the authentication prompt** during claiming (e.g., Face ID, Touch ID, or passcode prompt).
3. **Attempt to present the credential** using either:
* [Proximity presentation](/docs/holding/proximity-presentation-tutorial)
* [Remote presentation](/docs/holding/remote-presentation-tutorial)
4. **Verify the same authentication method** is required during presentation.
Testing with different authentication policies requires re-claiming credentials, as the policy is bound to the device key generated during the claiming process.
# URI scheme handling in your holder application
URL: /docs/holding/credential-claiming-guides/handling-uri-schemes
Description: Learn how to configure your holder application to handle different types of credential offer URI schemes from issuers
## Overview [#overview]
When issuers create credential offers, they can use different URI schemes to control which wallet applications can claim those offers and how the user experience flows. Your holder application needs to be properly configured to handle the URI schemes that issuers in your ecosystem are using.
This guide explains the different URI scheme types and shows you how to configure your holder application to handle each type.
## Understanding URI schemes in credential claiming [#understanding-uri-schemes-in-credential-claiming]
A URI scheme is the protocol part at the beginning of a URI (such as `https://`, `mailto://`, or custom schemes like `openid-credential-offer://`). When an issuer generates a credential offer, they choose which URI scheme to use based on their requirements for security, user experience, and control over which apps can handle the offer.
The three main URI scheme types used for credential offers are:
1. **Standard OID4VCI custom scheme** (`openid-credential-offer://`) - The baseline scheme defined by the OID4VCI specification. Any app registered to handle this scheme can respond to the offer.
2. **Private-use URI scheme** (`com.example.wallet://`) - A unique custom scheme using reverse-domain notation. This makes it less likely (but not impossible) for other apps to handle the offer.
3. **Claimed HTTPS scheme** (`https://example.com/wallet/...`) - Uses domain-verified App Links (Android) or Universal Links (iOS) to ensure only your specific app can handle offers from your domain.
For a detailed explanation of how these schemes work, their trade-offs, and security considerations, see the [Credential offer documentation](/docs/issuance/credential-offer/overview#controlling-how-credentials-are-claimed).
## Prerequisites [#prerequisites]
This guide assumes you have:
* Completed the [Credential Claiming Tutorial](/docs/holding/credential-claiming-tutorial) and have a working holder application. All examples in this guide build on top of the tutorial application.
* Understanding of how [credential offers](/docs/issuance/credential-offer/overview) are created and structured.
* Access to modify your application configuration and code.
For HTTPS schemes (App Links/Universal Links), you will also need:
* A domain you control.
* Ability to host verification files on your web server.
## Configuring URI scheme handling [#configuring-uri-scheme-handling]
The configuration required depends on which URI scheme types you want to support and which platform you're developing for.
### Standard OID4VCI custom scheme [#standard-oid4vci-custom-scheme]
The standard `openid-credential-offer://` scheme is already configured in the tutorial application. No additional setup is required unless you removed it during development.
**Verify scheme configuration**
1. Open your project in Xcode.
2. Select your app target and navigate to the **Info** tab.
3. Expand **URL Types** and verify an entry exists with:
* **Identifier**: `openid-credential-offer`
* **URL Schemes**: `openid-credential-offer`
**Verify intent filter configuration**
1. Open your `AndroidManifest.xml` file and verify the following intent filter exists within your main activity:
```xml title="AndroidManifest.xml"
```
**Verify scheme configuration**
1. Open your `app.config.ts` file and verify the scheme is properly configured:
```ts title="app.config.ts"
export default ({ config }: ConfigContext): ExpoConfig => ({
// ... other config
scheme: "openid-credential-offer",
// ... other config
});
```
For Expo projects, this configuration automatically sets up the scheme for both iOS and Android.
### Private-use URI scheme [#private-use-uri-scheme]
To handle offers using a custom scheme like `com.yourcompany.wallet://`, you need to register that unique scheme with the operating system.
**Step 1: Register your custom scheme**
1. Open your project in Xcode.
2. Select your app target and navigate to the **Info** tab.
3. Expand **URL Types** and select the **+** button.
4. Enter:
* **Identifier**: `com.yourcompany.wallet`
* **URL Schemes**: `com.yourcompany.wallet`
**Step 2: Handle incoming URLs**
In your `ContentView.swift` file, add a handler for the custom scheme URLs. Add the following extension to your ViewModel:
```swift title="ContentView.swift"
// MARK: Handle URL
extension ViewModel {
func handleIncomingURL(_ url: URL) {
guard url.scheme == "com.yourcompany.wallet" else { return }
// Extract the credential offer from the URL
// The offer is typically base64-encoded in the path or as a query parameter
if let offerString = extractCredentialOffer(from: url) {
discoverCredentialOffer(offerString)
navigationPath.append(NavigationState.credentialOffer)
}
}
func extractCredentialOffer(from url: URL) -> String? {
// Example: com.yourcompany.wallet://accept/BASE64_ENCODED_OFFER
if url.host == "accept",
let encodedOffer = url.pathComponents.last,
let data = Data(base64URLEncoded: encodedOffer),
let decodedOffer = String(data: data, encoding: .utf8) {
return decodedOffer
}
// Alternative: com.yourcompany.wallet://accept?offer=BASE64_ENCODED_OFFER
if let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let offerParam = components.queryItems?.first(where: { $0.name == "offer" })?.value,
let data = Data(base64URLEncoded: offerParam),
let decodedOffer = String(data: data, encoding: .utf8) {
return decodedOffer
}
return nil
}
}
// Helper extension for base64 URL decoding
extension Data {
init?(base64URLEncoded string: String) {
var base64 = string
.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
let paddingLength = (4 - base64.count % 4) % 4
base64.append(String(repeating: "=", count: paddingLength))
self.init(base64Encoded: base64)
}
}
```
Then, add an `onOpenURL` modifier in `ContentView` to handle incoming custom scheme URLs:
```swift title="ContentView.swift"
.onOpenURL { url in
viewModel.handleIncomingURL(url)
}
```
If your app already handles other types of links, you'll need to update the `handleIncomingURL` method to support multiple link types.
**Step 1: Add intent filter for custom scheme**
Open your `AndroidManifest.xml` file and add a new intent filter for your custom scheme:
```xml title="AndroidManifest.xml"
```
**Step 2: Handle incoming intents**
In your `MainActivity.kt` file, add handling for the custom scheme URLs:
```kotlin title="MainActivity.kt"
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Handle intent when activity is created
handleIntent(intent)
setContent {
// ... existing UI code
}
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
// Handle intent when activity is already running
handleIntent(intent)
}
private fun handleIntent(intent: Intent?) {
val data = intent?.data ?: return
when (data.scheme) {
"openid-credential-offer" -> {
// Standard scheme handling (already implemented in tutorial)
val credentialOffer = data.toString()
navigateToOfferScreen(credentialOffer)
}
"com.yourcompany.wallet" -> {
// Custom scheme handling
val credentialOffer = extractCredentialOffer(data)
if (credentialOffer != null) {
navigateToOfferScreen(credentialOffer)
}
}
}
}
private fun extractCredentialOffer(uri: Uri): String? {
// Example: com.yourcompany.wallet://accept/BASE64_ENCODED_OFFER
val encodedOffer = uri.lastPathSegment ?: uri.getQueryParameter("offer") ?: return null
return try {
val decodedBytes = android.util.Base64.decode(
encodedOffer.replace('-', '+').replace('_', '/'),
android.util.Base64.URL_SAFE or android.util.Base64.NO_PADDING
)
String(decodedBytes, Charsets.UTF_8)
} catch (e: IllegalArgumentException) {
null
}
}
private fun navigateToOfferScreen(credentialOffer: String) {
// Navigate to your offer screen with the credential offer
// Implementation depends on your navigation setup
}
}
```
**Step 1: Update scheme configuration**
Open your `app.config.ts` file and update the scheme to your custom scheme:
```ts title="app.config.ts"
export default ({ config }: ConfigContext): ExpoConfig => ({
// ... other config
scheme: "com.yourcompany.wallet",
// ... other config
});
```
**Step 2: Handle incoming URLs**
In your `/app/_layout.tsx` file, add URL handling using Expo's Linking API:
```tsx title="/app/_layout.tsx"
import { useEffect } from "react";
import * as Linking from "expo-linking";
import { useRouter } from "expo-router";
export default function RootLayout() {
const router = useRouter();
useEffect(() => {
// Handle URL when app is already open
const subscription = Linking.addEventListener("url", ({ url }) => {
handleIncomingURL(url);
});
// Handle URL when app is opened from a closed state
Linking.getInitialURL().then((url) => {
if (url) {
handleIncomingURL(url);
}
});
return () => {
subscription.remove();
};
}, []);
const handleIncomingURL = (url: string) => {
const parsed = Linking.parse(url);
// Handle custom scheme URLs
if (parsed.scheme === "com.yourcompany.wallet" && parsed.hostname === "accept") {
const credentialOffer = extractCredentialOffer(url);
if (credentialOffer) {
router.push({
pathname: "/claim-credential",
params: { scannedValue: credentialOffer },
});
}
}
};
const extractCredentialOffer = (url: string): string | null => {
try {
const parsed = Linking.parse(url);
// Extract from path: com.yourcompany.wallet://accept/BASE64_ENCODED_OFFER
const encodedOffer =
(parsed.path ? parsed.path.split("/").filter(Boolean).pop() : undefined) ??
parsed.queryParams?.offer;
if (!encodedOffer) return null;
// Decode base64 URL-encoded offer
const base64 = encodedOffer
.replace(/-/g, "+")
.replace(/_/g, "/")
.padEnd(encodedOffer.length + ((4 - (encodedOffer.length % 4)) % 4), "=");
return atob(base64);
} catch (error) {
console.error("Failed to extract credential offer:", error);
return null;
}
};
return (
);
}
```
**Step 3: Rebuild native projects**
After changing the scheme configuration, regenerate the native projects:
```bash title="Regenerate native projects"
yarn expo prebuild --clean
```
### Claimed HTTPS scheme (App Links / Universal Links) [#claimed-https-scheme-app-links--universal-links]
HTTPS schemes provide the most secure and reliable way to ensure only your specific app handles credential offers from your domain. This requires domain ownership and proper configuration.
**Step 1: Create the Apple App Site Association file**
This step must be performed by the Credential Issuer or the party controlling the domain used in the HTTPS scheme, as it requires hosting a specific file on the web server. If you are both the issuer and holder, you can complete this step yourself. Otherwise, you will need to coordinate with the issuer to ensure this file is created and hosted correctly.
Create a file named `apple-app-site-association` (no file extension) with the following content:
```json title="apple-app-site-association"
{
"applinks": {
"apps": [],
"details": [
{
"appID": "TEAM_ID.com.yourcompany.wallet",
"paths": ["/wallet/*"]
}
]
}
}
```
Replace:
* `TEAM_ID` with your Apple Developer Team ID (found in your Apple Developer account).
* `com.yourcompany.wallet` with your app's bundle identifier.
* `/wallet/*` with the path pattern you want to handle (you can use multiple paths).
**Step 2: Host the association file**
Upload the file to your web server at:
```
https://yourdomain.com/.well-known/apple-app-site-association
```
Or directly at the root:
```
https://yourdomain.com/apple-app-site-association
```
Ensure:
* The file is served with `Content-Type: application/json`.
* The file is accessible via HTTPS without redirects.
* No `.json` extension is added to the filename.
**Step 3: Enable Associated Domains capability**
1. Open your project in Xcode.
2. Select your app target and navigate to **Signing & Capabilities**.
3. Select **+ Capability** and add **Associated Domains**.
4. Add your domain in the format: `applinks:yourdomain.com`
**Step 4: Handle Universal Links**
In your `ContentView.swift` file, add a handler for Universal Links. Add the following extension to your ViewModel:
```swift title="ContentView.swift"
// MARK: Handle Universal Links
extension ViewModel {
func handleIncomingURL(_ url: URL) {
guard url.scheme == "https" && url.host == "yourdomain.com" else { return }
// Extract the credential offer from the URL
// Example: https://yourdomain.com/wallet/accept?offer=BASE64_ENCODED_OFFER
if let offerString = extractCredentialOffer(from: url) {
discoverCredentialOffer(offerString)
navigationPath.append(NavigationState.credentialOffer)
}
}
func extractCredentialOffer(from url: URL) -> String? {
// Verify path contains required components
guard url.pathComponents.contains("wallet"),
url.pathComponents.contains("accept") else { return nil }
// Extract offer from query parameter
if let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let offerParam = components.queryItems?.first(where: { $0.name == "offer" })?.value,
let data = Data(base64URLEncoded: offerParam),
let decodedOffer = String(data: data, encoding: .utf8) {
return decodedOffer
}
return nil
}
}
// Helper extension for base64 URL decoding
extension Data {
init?(base64URLEncoded string: String) {
var base64 = string
.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
let paddingLength = (4 - base64.count % 4) % 4
base64.append(String(repeating: "=", count: paddingLength))
self.init(base64Encoded: base64)
}
}
```
Then, add an `onOpenURL` modifier in `ContentView` to handle incoming universal links:
```swift title="ContentView.swift"
.onOpenURL { url in
viewModel.handleIncomingURL(url)
}
```
If your app already handles other types of links, you'll need to update the `handleIncomingURL` method to support multiple link types.
**Step 1: Create the Digital Asset Links file**
This step must be performed by the Credential Issuer or the party controlling the domain used in the HTTPS scheme, as it requires hosting a specific file on the web server. If you are both the issuer and holder, you can complete this step yourself. Otherwise, you will need to coordinate with the issuer to ensure this file is created and hosted correctly.
Create a file named `assetlinks.json` with the following content:
```json title="assetlinks.json"
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.yourcompany.wallet",
"sha256_cert_fingerprints": [
"YOUR_APP_SHA256_FINGERPRINT"
]
}
}
]
```
Replace:
* `com.yourcompany.wallet` with your app's package name.
* `YOUR_APP_SHA256_FINGERPRINT` with your app's SHA-256 certificate fingerprint.
To get your SHA-256 fingerprint, run:
```bash title="Get SHA-256 fingerprint"
keytool -list -v -keystore your-release-key.keystore
```
Or for debug builds:
```bash title="Get debug SHA-256 fingerprint"
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
```
**Step 2: Host the association file**
Upload the file to your web server at:
```
https://yourdomain.com/.well-known/assetlinks.json
```
Ensure:
* The file is served with `Content-Type: application/json`.
* The file is accessible via HTTPS without redirects.
**Step 3: Add App Links intent filter**
Open your `AndroidManifest.xml` and add an intent filter with `android:autoVerify="true"`:
```xml title="AndroidManifest.xml"
```
**Step 4: Handle App Links**
Update your `MainActivity.kt` to handle App Links:
```kotlin title="MainActivity.kt"
class MainActivity : ComponentActivity() {
private fun handleIntent(intent: Intent?) {
val data = intent?.data ?: return
when {
// Handle App Links (HTTPS URLs)
data.scheme == "https" && data.host == "yourdomain.com" -> {
handleAppLink(data)
}
// Handle custom schemes
data.scheme == "openid-credential-offer" || data.scheme == "com.yourcompany.wallet" -> {
handleCustomScheme(data)
}
}
}
private fun handleAppLink(uri: Uri) {
// Example: https://yourdomain.com/wallet/accept?offer=BASE64_ENCODED_OFFER
if (uri.pathSegments.contains("wallet") && uri.pathSegments.contains("accept")) {
val encodedOffer = uri.getQueryParameter("offer") ?: return
val credentialOffer = extractCredentialOffer(encodedOffer)
if (credentialOffer != null) {
navigateToOfferScreen(credentialOffer)
}
}
}
private fun handleCustomScheme(uri: Uri) {
// ... existing custom scheme handling
}
private fun extractCredentialOffer(encodedOffer: String): String? {
return try {
val decodedBytes = android.util.Base64.decode(
encodedOffer.replace('-', '+').replace('_', '/'),
android.util.Base64.URL_SAFE or android.util.Base64.NO_PADDING
)
String(decodedBytes, Charsets.UTF_8)
} catch (e: IllegalArgumentException) {
null
}
}
}
```
**Step 5: Verify App Links**
After installing your app, verify that App Links are working:
```bash title="Verify App Links"
adb shell am start -a android.intent.action.VIEW -d "https://yourdomain.com/wallet/accept?offer=test"
```
You can also check the verification status:
```bash title="Check verification status"
adb shell pm get-app-links com.yourcompany.wallet
```
For React Native with Expo, configuring Universal Links (iOS) and App Links (Android) requires additional setup in the app configuration.
**Step 1: Create association files**
This step must be performed by the Credential Issuer or the party controlling the domain used in the HTTPS scheme, as it requires hosting a specific file on the web server. If you are both the issuer and holder, you can complete this step yourself. Otherwise, you will need to coordinate with the issuer to ensure this file is created and hosted correctly.
Follow the instructions in the iOS and Android tabs to create and host:
* `apple-app-site-association` file at `https://yourdomain.com/.well-known/apple-app-site-association`
* `assetlinks.json` file at `https://yourdomain.com/.well-known/assetlinks.json`
**Step 2: Configure app for Universal Links / App Links**
Update your `app.config.ts`:
```ts title="app.config.ts"
export default ({ config }: ConfigContext): ExpoConfig => ({
// ... other config
ios: {
bundleIdentifier: "com.yourcompany.wallet",
associatedDomains: ["applinks:yourdomain.com"],
// ... other iOS config
},
android: {
package: "com.yourcompany.wallet",
intentFilters: [
{
action: "VIEW",
autoVerify: true,
data: [
{
scheme: "https",
host: "yourdomain.com",
pathPrefix: "/wallet",
},
],
category: ["BROWSABLE", "DEFAULT"],
},
],
// ... other Android config
},
// ... other config
});
```
**Step 3: Handle Universal Links / App Links**
Update your `/app/_layout.tsx` to handle HTTPS URLs:
```tsx title="/app/_layout.tsx"
import { useEffect } from "react";
import * as Linking from "expo-linking";
import { useRouter } from "expo-router";
export default function RootLayout() {
const router = useRouter();
useEffect(() => {
const subscription = Linking.addEventListener("url", ({ url }) => {
handleIncomingURL(url);
});
Linking.getInitialURL().then((url) => {
if (url) {
handleIncomingURL(url);
}
});
return () => {
subscription.remove();
};
}, []);
const handleIncomingURL = (url: string) => {
const parsed = Linking.parse(url);
// Handle Universal Links / App Links (HTTPS)
if (parsed.scheme === "https" && parsed.hostname === "yourdomain.com") {
handleAppLink(url);
return;
}
// Handle custom schemes
if (
parsed.scheme === "openid-credential-offer" ||
parsed.scheme === "com.yourcompany.wallet"
) {
handleCustomScheme(url);
return;
}
};
const handleAppLink = (url: string) => {
const parsed = Linking.parse(url);
// Example: https://yourdomain.com/wallet/accept?offer=BASE64_ENCODED_OFFER
if (parsed.path?.includes("/wallet/accept")) {
const encodedOffer = parsed.queryParams?.offer as string;
if (encodedOffer) {
const credentialOffer = extractCredentialOffer(encodedOffer);
if (credentialOffer) {
router.push({
pathname: "/claim-credential",
params: { scannedValue: credentialOffer },
});
}
}
}
};
const handleCustomScheme = (url: string) => {
// ... existing custom scheme handling
};
const extractCredentialOffer = (encodedOffer: string): string | null => {
try {
const base64 = encodedOffer
.replace(/-/g, "+")
.replace(/_/g, "/")
.padEnd(encodedOffer.length + ((4 - (encodedOffer.length % 4)) % 4), "=");
return atob(base64);
} catch (error) {
console.error("Failed to decode credential offer:", error);
return null;
}
};
return (
);
}
```
**Step 4: Rebuild native projects**
After updating the configuration, regenerate the native projects:
```bash title="Regenerate native projects"
yarn expo prebuild --clean
```
**Step 5: Build and test**
For iOS, you must test Universal Links on a physical device (not simulator) with a production or ad-hoc build.
For Android, install the app and verify App Links as described in the Android tab above.
## Testing your URI scheme implementation [#testing-your-uri-scheme-implementation]
After configuring your app to handle different URI schemes, test each implementation to ensure it works correctly.
### Testing with QR codes [#testing-with-qr-codes]
Create a Credential offer and generate QR codes for each URI scheme type you support:
1. **Standard scheme**: `openid-credential-offer://?credential_offer=...`
2. **Custom scheme**: `com.yourcompany.wallet://accept/{base64UrlEncodedOffer}`
3. **HTTPS scheme**: `https://yourdomain.com/wallet/accept?offer={base64UrlEncodedOffer}`
You can use online QR code generators or create them programmatically for testing.
### Testing with deep links [#testing-with-deep-links]
For iOS, use the following command to test deep links on a connected device:
```bash title="Test iOS deep link"
xcrun simctl openurl booted "com.yourcompany.wallet://accept/BASE64_ENCODED_OFFER"
```
For Android, use:
```bash title="Test Android deep link"
adb shell am start -a android.intent.action.VIEW -d "com.yourcompany.wallet://accept/BASE64_ENCODED_OFFER"
```
### Testing Universal Links / App Links [#testing-universal-links--app-links]
For iOS Universal Links, you must test on a physical device with a production or ad-hoc build. Links opened in Safari should open your app.
For Android App Links, use:
```bash title="Test Android App Link"
adb shell am start -a android.intent.action.VIEW -d "https://yourdomain.com/wallet/accept?offer=BASE64_ENCODED_OFFER"
```
## Common issues and troubleshooting [#common-issues-and-troubleshooting]
### App doesn't open when scanning QR code or tapping link [#app-doesnt-open-when-scanning-qr-code-or-tapping-link]
**Possible causes:**
* URI scheme not properly registered in the app configuration.
* For Universal Links/App Links, association files not properly hosted or accessible.
* For Universal Links/App Links, domain not added to associated domains / intent filters.
**Solutions:**
* Verify URL scheme registration in your app configuration.
* Test the association file URLs directly in a browser to ensure they're accessible.
* Check that the association file content matches your app's bundle ID/package name and certificate.
* For iOS, verify Associated Domains capability is enabled and domains are correctly listed.
* For Android, verify `android:autoVerify="true"` is set and check verification status with `adb shell pm get-app-links`.
### Wrong app opens when multiple wallet apps are installed [#wrong-app-opens-when-multiple-wallet-apps-are-installed]
**Possible causes:**
* Multiple apps registered for the same URI scheme (common with `openid-credential-offer://`).
* Association files not properly verified (for Universal Links/App Links).
**Solutions:**
* Use a unique custom scheme or domain-verified HTTPS scheme.
* For Universal Links/App Links, ensure association files are correctly configured and verified.
* Test on a clean device or uninstall competing apps during testing.
### Encoded offer fails to decode [#encoded-offer-fails-to-decode]
**Possible causes:**
* Incorrect base64 URL encoding/decoding.
* Offer parameter not properly extracted from URL.
**Solutions:**
* Ensure you're using base64 URL encoding (not standard base64) with `-` and `_` instead of `+` and `/`.
* Verify padding is handled correctly when decoding.
* Log the encoded and decoded values to debug the transformation.
## Related resources [#related-resources]
* [iOS Universal Links documentation](https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app)
* [Android App Links documentation](https://developer.android.com/training/app-links)
* [OAuth 2.0 for Native Apps (RFC 8252)](https://datatracker.ietf.org/doc/html/rfc8252)
# How to implement mDocs revocation status checks in your holding application
URL: /docs/holding/credential-claiming-guides/revocation-status-check
## Overview [#overview]
This guide demonstrates how to implement revocation status checks for mDocs in your wallet applications. By implementing status checks, your wallet can verify whether credentials have been revoked or remain valid before displaying or presenting them.
MATTR's implementation of mDocs revocation is based on Draft 14 of the IETF [Token Status List](https://drafts.oauth.net/draft-ietf-oauth-status-list/draft-ietf-oauth-status-list.html) specification. A revocable mDoc includes a reference to a status list which is managed by the issuer. The list contains the revocation status of multiple credentials, and each credential references the index of its status within a specific status list.
Status lists are automatically created and managed by the issuer's MATTR VII tenant when issuing revocable mDocs. They are publicly available and can be consumed by holder applications to check the status of claimed mDocs.
For detailed information about how mDocs revocation works, including status list structure, tokens, and signing, see the [Revocation documentation](/docs/issuance/revocation/overview#mdocs).
## Prerequisites [#prerequisites]
This guide builds on knowledge from the [Credential Claiming tutorial](/docs/holding/credential-claiming-tutorial). It is recommended to complete that tutorial first, then return here to learn how to implement status checks.
## Understanding revocation status [#understanding-revocation-status]
### Status values [#status-values]
Revocable mDocs can have one of the following status values:
* **Valid**: The mDoc is valid and can be presented.
* **Invalid**: The mDoc is permanently revoked.- **Suspended**: The mDoc is temporarily revoked.
* **Unknown**: The status cannot be determined, typically because the status list has expired or is unavailable (e.g. holder is offline).
### How status information is stored [#how-status-information-is-stored]
When a revocable mDoc is issued, it includes a `status` object in its MSO payload that references a status list:
```json title="mDoc status reference"
// Rest of mDoc payload
"status": {
"status_list": {
"uri": "https://learn.vii.au01.mattr.global/v2/credentials/mobile/status-lists/f331c9be-f526-4577-bbac-ae93d6228f7a/token",
"idx": 0
}
}
```
* `status_list`: References the status list that holds the status information for this mDoc.
* `uri`: The publicly available endpoint where the status list token can be retrieved.
* `idx`: The index of this mDoc's status within the referenced status list.
When a verifier or holder retrieves a status list, the issuer cannot tell what specific mDoc they are checking the status for. This preserves holder privacy - the issuer does not know how often or to whom an mDoc is being presented.
## Status list caching and updates [#status-list-caching-and-updates]
When retrieving a status list, the response is a signed status list token (a [CBOR Web Token](https://datatracker.ietf.org/doc/html/rfc8392)) that includes:
* `iat`: Timestamp when the status list token was signed.
* `exp`: Expiry timestamp.
* `ttl`: Recommended duration in seconds before fetching a new token.
* `status_list`: The compressed status list containing the status of all mDocs included in this list.
Retrieving a status list for every credential operation would create performance issues and make offline presentation impossible. To address this, MATTR uses a caching mechanism based on the `ttl` and `exp` values:
* After retrieving a status list, the SDK will not fetch it again until the `ttl` has passed, as there are unlikely to be any changes. This optimizes performance and reduces unnecessary network requests.
* If the SDK fails to retrieve an updated status list **after the TTL** (for example, because the device is offline), it can continue using the cached status until the status list token expiry date (`exp`).
* If the **expiry date** passes without a successful update, the credential status can no longer be trusted and it is changed to `Unknown`. It is then up to the application to decide how to handle credentials with `Unknown` status.
### When TTL and EXP changes take effect [#when-ttl-and-exp-changes-take-effect]
If the issuer updates the status list TTL and/or EXP settings, the new values apply to the entire status list the next time it is generated and signed.
When the holder SDK next retrieves that status list, it will receive the updated token with the new TTL and EXP values. Until then, the SDK continues to use the previously cached status list with its original TTL and EXP values.
**Example scenario**:
* Credential is issued with a 24-hour TTL (default).
* The TTL configuration is changed to 2 hours by the issuer.
* The Holder's SDK will continue to use the cached status list with the original 24-hour TTL.
* The SDK will only receive the updated 2-hour TTL after it fetches a newly signed status list.
## Implementing status checks [#implementing-status-checks]
The Holder SDK **always checks the revocation status** for credentials that support revocation. This cannot be disabled.
When calling the `getCredential` method, you can control whether the SDK should attempt to retrieve an updated status list online using the `fetchUpdatedStatusList` parameter:
```swift title="Controlling status list updates"
// Retrieve updated status list online (default behavior)
let credential = try await mobileCredentialHolder.getCredential(
credentialId: credentialId,
fetchUpdatedStatusList: true
)
// Use valid cached status list only (skip online update)
let credential = try await mobileCredentialHolder.getCredential(
credentialId: credentialId,
fetchUpdatedStatusList: false
)
```
```kotlin title="Controlling status list updates"
// Retrieve updated status list online (default behavior)
val credential = mobileCredentialHolder.getCredential(
credentialId = credentialId,
fetchUpdatedStatusList = true
)
// Use valid cached status list only (skip online update)
val credential = mobileCredentialHolder.getCredential(
credentialId = credentialId,
fetchUpdatedStatusList = false
)
```
```tsx title="Controlling status list updates"
// Retrieve updated status list online (default behavior)
const credentialResult = await mobileCredentialHolder.getCredential(
credentialId,
{ fetchUpdatedStatusList: true }
)
if (credentialResult.isErr()) {
const { error } = credentialResult
// handle error scenarios
return
}
const credential = credentialResult.value
// Use valid cached status list only (skip online update)
const credentialResult = await mobileCredentialHolder.getCredential(
credentialId,
{ fetchUpdatedStatusList: false }
)
if (credentialResult.isErr()) {
const { error } = credentialResult
// handle error scenarios
return
}
const credential = credentialResult.value
```
**Important**: Setting `fetchUpdatedStatusList: false` does not disable status checking. The SDK will still check the credential's status using the cached status list (as long as it is valid). This parameter only controls whether the SDK attempts to fetch an updated status list online.
### Status check flow [#status-check-flow]
1. When `getCredential` is called, the SDK checks the `fetchUpdatedStatusList` parameter.
2. **If `fetchUpdatedStatusList` is `true`** (default), the SDK checks whether the currently cached status list has passed its TTL:
* If the TTL has **not** passed, the cached status list can be used. The SDK skips the online fetch and proceeds to step 3.
* If the TTL **has** passed, the cached status list cannot be used. The SDK attempts to fetch an updated copy of the status list:
* If the fetch **succeeds**, the retrieved status list is stored by the SDK as the new cached status list.
* If the fetch **fails** (e.g., device is offline), no changes are made to the existing cached status list, and the SDK falls through to step 3.
3. **The SDK evaluates the cached status list**:
* If a valid (non-expired) cached status list is **available**, the SDK will use it to check the credential's status and return it.
* If the cached status list has **expired** (e.g. EXP has passed) it cannot be relied upon, and the SDK will return the status as `Unknown`.
* If **no cached status list exists** (e.g., the status list was never successfully retrieved), the SDK will return the status as `Unknown`.
## Checking credential status [#checking-credential-status]
After retrieving a credential, you can check its revocation status to determine whether it should be displayed or presented based on verification failure types:
```swift title="Checking credential status"
let credential = try await mobileCredentialHolder.getCredential(
credentialId: credentialId
)
if let verificationResult = credential.verificationResult, verificationResult.verified {
print("Credential is valid and can be presented")
// Display credential and allow presentation
} else {
switch credential.verificationResult?.failureType {
case .statusRevoked:
print("Credential has been permanently revoked")
// Display warning, prevent presentation
case .statusSuspended:
// Deprecated: only applies to legacy credentials issued before Draft 14 of the Token Status List specification
print("Credential has been temporarily suspended")
// Display warning, prevent presentation
case .statusUnknown:
print("Credential status cannot be determined")
// Display warning, handle according to your security requirements
default:
// Handle other failure types
break
}
}
```
```kotlin title="Checking credential status"
val verificationResult = credential.verificationResult
if (verificationResult?.verified == true) {
Log.d("Tag", "Credential is valid.")
} else {
when (verificationResult?.failureType) {
MobileCredentialVerificationFailureType.StatusRevoked -> {
Log.d("Tag", "Credential has been revoked.")
// Display warning, prevent presentation
}
// Deprecated: StatusSuspended only applies to legacy credentials issued before Draft 14 of the Token Status List specification
MobileCredentialVerificationFailureType.StatusSuspended -> {
Log.d("Tag", "Credential has been suspended.")
// Display warning, prevent presentation
}
MobileCredentialVerificationFailureType.StatusUnknown -> {
Log.d("Tag", "Credential status is unknown.")
// Display warning, handle according to your security requirements
}
else -> {
// Handle other failure types
}
}
}
```
```tsx title="Checking credential status"
const credentialResult = await mobileCredentialHolder.getCredential(
credentialId
)
if (credentialResult.isErr()) {
const { error } = credentialResult
// handle error scenarios
return
}
const credential = credentialResult.value
const verificationResult = credential.verificationResult
if (verificationResult?.verified === true) {
console.log("Credential is valid and can be presented")
// Display credential and allow presentation
} else {
switch (verificationResult?.failureType) {
case MobileCredentialVerificationFailureType.StatusRevoked:
console.log("Credential has been permanently revoked")
// Display warning, prevent presentation
break
// Deprecated: StatusSuspended only applies to legacy credentials issued before Draft 14 of the Token Status List specification
case MobileCredentialVerificationFailureType.StatusSuspended:
console.log("Credential has been temporarily suspended")
// Display warning, prevent presentation
break
case MobileCredentialVerificationFailureType.StatusUnknown:
console.log("Credential status cannot be determined")
// Display warning, handle according to your security requirements
break
default:
// Handle other failure types
break
}
}
```
## Handling offline scenarios [#handling-offline-scenarios]
* **Cache reliance**: When offline, the SDK relies on cached status lists.
* **Cached status after TTL**: If the device is offline and `ttl` has passed (but expiry hasn't), the cached status will be used.
* **Unknown status after expiry**: If the device is offline and `exp` has passed, the status will always be returned as `Unknown`.
* **Unknown status handling**: Define your application's policy for handling credentials with `Unknown` status. Options include:
* Preventing presentation.
* Allowing presentation with a warning.
* Allowing presentation only for recently checked credentials.
* **User communication**: Clearly inform users when status checks fail and what it means for their credentials.
# Wallet Attestation
URL: /docs/holding/credential-claiming-guides/wallet-attestation
Description: Learn how Wallet Attestation works in MATTR Holder SDKs to enable credential claiming from issuers that require trusted wallet applications.
## Overview [#overview]
Wallet Attestation lets your holder application prove it is a trusted, verified wallet so that issuers
who require it will release credentials to you. When an issuer requires Wallet Attestation, your
application must cryptographically prove its authenticity before it can claim credentials.
Your wallet is identified to issuers using a client identifier (`clientId`) you agree on with each issuer, which lets them recognise your wallet as an approved, trusted origin on their curated list. The Holder SDK automatically generates, manages, and presents the Wallet Attestation proof whenever a credential request occurs, so supporting this capability adds no extra code to your claiming flow and no extra friction for your users — everything happens in the background, and credential claiming looks and feels the same as before. The main work is the one-time coordination with each issuer to establish trust, described in [Setting up Wallet Attestation](#setting-up-wallet-attestation).
This mechanism is valuable when:
* **Enforcing wallet policies**: Issuers can ensure that only authorized and trusted wallet
applications receive their credentials.
* **Protecting against unauthorized access**: Credential theft is prevented by verifying the
wallet's identity before issuance.
* **Leveraging platform-based attestation**: Platform-specific security mechanisms (such as iOS App
Attestation and Android Key Attestation) are used to authenticate app instances with the MATTR VII
tenant, ensuring that only trusted wallet instances receive attestation proofs for use with
issuers.
For a detailed technical breakdown of Wallet Attestation from an issuer's perspective, including
attestation modes, JWT structures, and certificate trust chains, see
[Wallet Attestation](/docs/issuance/credential-issuance/wallet-attestation) in the Issuance documentation.
### What this means for your team [#what-this-means-for-your-team]
Supporting Wallet Attestation in your wallet involves more than just the integration code:
* **For policy makers**: Wallet Attestation is how issuers decide which wallet applications can receive their credentials. Establishing your wallet as a recognised, trusted application — through the root certificate you share and the client identifier you agree on — is what gets your users' credentials released by issuers that enforce these rules.
* **For product managers and designers**: Your existing claiming flows and user experiences stay the same. Attestation proofs are securely generated and presented in the background, so users gain the trust benefits without any added friction or change to how they claim credentials.
* **For solution architects**: Integrate the Holder SDK with your MATTR VII tenant and wallet identity. Securely manage identities and cryptographic keys, and design for multi-tenant separation, high availability, observability, error handling, and monitoring of attestation outcomes.
## Standards and specifications [#standards-and-specifications]
Wallet Attestation in MATTR VII is based on the following standards:
* [OAuth 2.0 Attestation-Based Client Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/): The core specification for Wallet Attestation
* [RFC 9449: DPoP (Demonstrating Proof-of-Possession)](https://www.rfc-editor.org/rfc/rfc9449.html): DPoP specification
## How it works [#how-it-works]
At a high level, getting your wallet recognised and claiming credentials from an issuer that requires Wallet Attestation involves four steps:
1. **Share and onboard the root certificate**: You create a Holder root CA certificate on your MATTR VII tenant — this is your Wallet Attestation root — and share it with the issuer, who onboards it as the trust anchor for your wallet application. This is a one-time, out-of-band setup per issuer.
2. **Configure and operate**: The issuer configures their issuance flows to require Wallet Attestation for your wallet, registered against the client identifier you agree on.
3. **Retrieve a credential as usual**: When a user claims a credential, the Holder SDK automatically obtains or refreshes a Wallet Attestation proof — bound to that specific wallet instance — and sends it to the issuer as part of the standard credential retrieval flow.
4. **Verify and issue**: The issuer validates the proof against your onboarded root CA certificate. If it is valid and your wallet is approved, the credential is issued and stored in the app; otherwise the SDK surfaces an error.
Steps 1 and 2 are the one-time coordination covered in [Setting up Wallet Attestation](#setting-up-wallet-attestation). Steps 3 and 4 are handled automatically by the SDK at claim time and require no additional code from you. The rest of this section explains the underlying interaction in detail.
The following diagram illustrates the interaction between the Holder SDK, the MATTR VII tenant, and the issuer.
At the protocol level, the Wallet Attestation proof is carried as an OAuth **client attestation
JWT** (defined in [OAuth 2.0 Attestation-Based Client Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/))
combined with a **DPoP proof**. In the descriptions below, "client attestation JWT" refers to the
token that conveys the Wallet Attestation to the issuer.
1. **Retrieve the credential offer**: The Holder SDK retrieves the [credential offer](/docs/issuance/credential-offer/overview) from the
issuer. This offer includes a reference to the issuer's metadata endpoint.
2. **Check issuer metadata for attestation requirements**: The Holder SDK fetches the issuer's metadata. If the metadata specifies that the issuer requires
Wallet Attestation to issue credentials, the SDK proceeds with the attestation flow. If not,
credential claiming continues without attestation.
3. **Request a Wallet Attestation token from the tethered tenant**: The Holder SDK automatically makes a request to the MATTR VII tenant it is tethered to, passing
the identifiers of the Holder Application configuration that were declared during SDK initialization.
4. **The MATTR VII tenant validates and returns the Wallet Attestation token**: The MATTR VII tenant recognizes the app as a registered instance of the configured Holder
Application. Assuming SDK Tethering is correctly set up, the tenant returns a signed client attestation JWT that carries the Wallet Attestation proof.
5. **Generate a DPoP proof and present attestation to the issuer**: The Holder SDK generates a DPoP proof locally and includes both the client attestation JWT and the
DPoP proof when requesting the credential from the issuer. Together these two values form the
complete Wallet Attestation presented to the issuer. No additional code is required from the
holder application developer.
6. **Issuer validates and issues the credential**: The issuer validates the Wallet Attestation (client attestation JWT + DPoP proof), confirms that all requirements are
met, and issues the credential to the holder application.
## Setting up Wallet Attestation [#setting-up-wallet-attestation]
Complete the following steps to enable Wallet Attestation in your holder application.
### Configure SDK Tethering [#configure-sdk-tethering]
Set up [SDK Tethering](/docs/holding/sdk-operations/sdk-tethering) to connect the Holder SDK to your
MATTR VII tenant:
1. **Create Holder Applications** on your MATTR VII tenant for each platform target (iOS and
Android).
2. **Initialize the SDK** with the correct `platformConfiguration` including the tenant URL and
the identifiers of the Holder Application you created.
### Create and activate a Holder root CA certificate [#create-and-activate-a-holder-root-ca-certificate]
Your tethered MATTR VII tenant must have an active Holder root CA certificate. This root CA is
used to sign Wallet Attestation signers, which in turn sign the client attestation JWTs presented
to issuers, forming a [chain of trust](/docs/holding/certificates/overview).
MATTR VII supports two types of Holder root CA certificates:
* **Managed**: MATTR VII generates and manages the root certificate and its private key on your
behalf. Wallet attestation signers are auto-provisioned when needed.
* **Unmanaged (external)**: You supply your own root CA in PEM format. You are responsible for
signing and uploading wallet attestation signer certificates.
For step-by-step instructions on creating and activating a Holder root CA, see the
[Holder certificates guide](/docs/holding/certificates/guide).
### Coordinate with the issuer [#coordinate-with-the-issuer]
Wallet Attestation requires an out-of-band coordination process between you and each credential
issuer. You must complete the following before your wallet can claim credentials from issuers that
require Wallet Attestation:
1. **Confirm the issuer's requirements**: Verify with the issuer whether Wallet Attestation is
required. See
[Understanding issuer requirements](#understanding-issuer-requirements) for details on how
issuer-side configuration works.
2. **Share your Holder root CA certificate**: Provide the **public certificate** of your root CA
to the issuer (for example, via a secure channel or as part of a business onboarding process).
The issuer uses this certificate so they can validate the
attestation proof chain your SDK presents.
3. **Align on the client identifier**: The `clientId` you configure on your Holder Application is
included as the `sub` claim in the wallet attestation JWT and is the value the issuer sees when
your wallet claims a credential. The issuer must have this exact value registered against your
wallet (with Wallet Attestation required) in their trusted wallet list. If the value your wallet
presents and the value the issuer has registered do not match, the issuer rejects the attestation
proof and credential claiming fails. See
[Agreeing on a client identifier](#agreeing-on-a-client-identifier) for how to coordinate this
value and why agreeing on it early matters.
## Agreeing on a client identifier [#agreeing-on-a-client-identifier]
The Wallet Attestation proof already tells the issuer which trusted wallet application is requesting
the credential. That proof is what cryptographically establishes your wallet's authenticity. The
`clientId` carries no additional product capability on top of this; it exists because the underlying
[OAuth 2.0 Attestation-Based Client Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/)
specification requires every client to identify itself with a `client_id`. MATTR VII supports it so
your wallet interoperates with any spec-compliant issuer, but the choice of value is something you
and each issuer must agree on out of band.
There are two ways this coordination plays out:
* **You define the client identifier (recommended)**: You choose a single `clientId` for your wallet
application (for example, `mattr-wallet`), configure it on your Holder Application, and share it,
together with your [Holder root CA certificate](#create-and-activate-a-holder-root-ca-certificate),
with each issuer during onboarding. Every issuer registers that same value, so your wallet
presents one consistent identity everywhere. This keeps your integration simple: one Holder
Application, one `clientId`, and no per-issuer bookkeeping.
* **The issuer assigns the client identifier**: Some issuers insist on assigning their own value
(for example, one issuer registers your wallet as `mattr-wallet` while another requires
`partner-wallet-42`). Because the value your wallet presents must match what each issuer has
registered, supporting issuer-assigned identifiers means maintaining a **separate Holder
Application for each distinct `clientId`** and tracking which issuer expects which value. This
adds operational overhead and is best avoided where possible.
**Recommendation**: Wherever you can, agree on a single `clientId` that **you** define and ask each
issuer to register it as-is. Raising this early in onboarding, before the issuer has provisioned
your wallet in their trusted wallet list, saves you from managing multiple Holder Applications and
per-issuer client identifier mappings later.
**Important for existing integrations**: If you already have a `client_id` registered with an
issuer from a pre-Wallet Attestation integration, we recommend registering a **new `client_id`**
for your Wallet Attestation-enabled app rather than reusing the existing one. See
[Migrating to Wallet Attestation](#migrating-to-wallet-attestation) for details.
Once these steps are complete, the SDK handles all Wallet Attestation interactions automatically
whenever an issuer requires it during credential claiming.
## Understanding issuer requirements [#understanding-issuer-requirements]
Whether an issuer requires Wallet Attestation from your wallet depends on how they have configured
their trusted wallets, not just on what their public metadata advertises.
* **Issuer metadata** (the OID4VCI [`.well-known` endpoint](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-credential-issuer-metadata-)) declares which authentication methods the
issuer **supports** at a tenant level. For example, the issuer may advertise both
`attest_jwt_client_auth_dpop` and `none` as supported methods.
* **Per-client configuration** determines what is **required** for any specific wallet. The
issuer configures this in their trusted wallet list, associating a client identifier with a
specific authentication method.
Your wallet cannot determine from public metadata alone whether Wallet Attestation is required for
your specific wallet application. The issuer communicates this requirement to you as part of the
onboarding process. In practice:
* If the issuer has configured your application with Wallet Attestation required, the SDK
**must** present a valid attestation proof or credential claiming will fail.
* If the issuer has configured your application without Wallet Attestation, the SDK may still
send an attestation proof (if it detects attestation support in the issuer metadata), and the
issuer will simply ignore it.
* If the issuer does not support Wallet Attestation at all (not advertised in metadata), the SDK
does not send an attestation proof.
## Migrating to Wallet Attestation [#migrating-to-wallet-attestation]
When you enable Wallet Attestation, your SDK uses a different client authentication method
(`attest_jwt_client_auth_dpop` instead of `none`). If older app versions that do not support
Wallet Attestation share the same client identifier, and the issuer configures that client identifier to
require attestation, those older versions will fail to claim credentials.
If your holder application is already integrated with an issuer that **does
not** require Wallet Attestation, it is recommended to use a new client identifier to
migrate safely without breaking existing app versions, as described in the following steps.
### Register a new client identifier with the issuer [#register-a-new-client-identifier-with-the-issuer]
Coordinate with the issuer to register a new client identifier (for example, `my-wallet-client-v2`) that will
be associated with Wallet Attestation. The issuer adds this new client identifier to their trusted
wallet list with Wallet Attestation required, alongside your existing client identifier which continues
to work without attestation.
### Configure your Holder Application with the new client identifier [#configure-your-holder-application-with-the-new-client-identifier]
When creating or updating your Holder Application on your MATTR VII tenant, set the `clientId`
field to the new value you registered with the issuer:
```json title="Request body"
{
"name": "My iOS Holder Application",
"clientId": "my-wallet-client-v2", // [!code focus]
"type": "ios",
"bundleId": "com.yourcompany.holderapp",
"teamId": "YOUR_APPLE_TEAM_ID",
"maxTimeOfflineInSecs": 864000,
"appAttest": {
"required": true,
"environment": "production"
}
}
```
### Release the updated app [#release-the-updated-app]
Publish the new version of your app that supports Wallet Attestation. This version uses the new client identifier
and presents Wallet Attestation proofs to issuers that support it. Existing app versions continue
to use the original client identifier and operate without attestation.
### Retire the old client identifier [#retire-the-old-client-identifier]
Once you are confident that enough users have migrated to the new app version, coordinate with the
issuer to remove the old client identifier from their trusted wallet list. This completes the migration
and ensures all active wallet instances use Wallet Attestation.
# Getting started with the MATTR GO Hold example app
URL: /docs/holding/go-hold/getting-started
### Download [#download]
Download the MATTR GO Hold example app to your mobile device from:
* The [App Store](https://apps.apple.com/us/app/mattr-wallet/id1518660243) for iOS devices.
* [Google Play](https://play.google.com/store/apps/details?id=global.mattr.wallet) for Android
devices.
## Unlock [#unlock]
Once you open the app, you will need to unlock it. The app uses your device configured
authentication method to unlock. This can be either a passcode or biometric authentication.
Once the app is unlocked, you can proceed with completing the onboarding instructions.
You will need to use your device unlock method every time you access the app
and for any interactions that require authentication.
## Setup [#setup]
You will be prompted to enable push notifications, as the app can notify you when new credentials
are available or when the status of an existing credential has changed.
If you choose not to enable push notifications during onboarding, you can do so later by accessing
the **Settings** menu and setting *Notifications* to `On` under *General*.
## Claim a credential [#claim-a-credential]
Perform the following steps to claim a non-production [mDoc](/docs/concepts/mdocs) into your example app:
1. Tap the Blue **Share** button.
2. Select **Respond or Collect**. This will open the camera view (You may need to allow the app to
access your camera).
3. Scan the following QR code:
4. Follow the on-screen instructions to claim the credential.
Note that this workflow requires an active internet connection.
## Present a credential online [#present-a-credential-online]
Experience presenting a digital credential online using your GO Hold example app by visiting our
[Demo hub](https://mattr.global/demo-hub#demos). Follow the guided steps to see how easy it is to
share your credentials securely in a real-world scenario.
For the best experience, explore both cross-device (scan a QR code from your
phone) and same-device (open the link directly on your phone) flows.
## Explore [#explore]
Explore the different app functionalities:
* **Wallet tab**: Shows all credentials that are available in your wallet. It also enables sorting
and filtering the list using the icon on the top-right corner.
* **Activity tab**: Shows the complete activity history for your wallet. It also enables filtering
the displayed events using the icon on the top-right corner.
* **Settings tab**: Includes different app settings.
* **Interaction button**: Used to claim new credentials or share existing ones by choosing one of
the following options:
* **Share Credential**: Enables selecting an mDoc and generating a QR code to present it to a
verifier via a [proximity presentation flow](/docs/verification/in-person-overview) as per
ISO/IEC 18013-5.
* **Respond or Collect**: Enables scanning a QR code to claim a credential via an
[OID4VCI](/docs/issuance/oid4vci-overview) workflow.
## Dive deeper [#dive-deeper]
* For issuers:
* [OID4VCI Authorization Code tutorial](/docs/issuance/authorization-code/tutorial): Use the MATTR GO Hold app to claim an mDoc via an OID4VCI Authorization Code flow.
* [OID4VCI Pre-authorized Code tutorial](/docs/issuance/pre-authorized-code/tutorial): Use the MATTR GO Hold app to claim an mDoc via an OID4VCI Pre-authorized Code flow.
* For verifiers:
* [Remote web verification tutorial](/docs/verification/remote-web-verifiers/tutorial): Use the MATTR GO Hold app to present a credential remotely to a web application.
* [Remote mobile verification tutorial](/docs/verification/remote-mobile-verifiers/tutorial): Use the MATTR GO Hold app to present a credential remotely to a mobile application.
* Download the [MATTR GO Verify](/docs/verification/go-verify/getting-started) example app on
a different mobile device and verify credentials stored on your GO Hold example app via a
[proximity verification flow](/docs/verification/in-person-overview).
# Libraries in use
URL: /docs/holding/go-hold/libraries
The following lists all notices for third party software that may be used in some way by MATTR GO
Wallets, the MATTR GO Hold example app and other development tools. We value the contributions by
open source developers and thank them.
# System requirements
URL: /docs/holding/go-hold/system-requirements
Description: Minimum requirements and supported devices for the MATTR GO Hold example app.
## Operating systems [#operating-systems]
The following operating system versions represent the minimum supported platforms for MATTR GO Hold.
* iOS 15 or higher
* Android 7 (API level 24) or higher
MATTR validates functionality using currently supported operating system releases provided by Apple and Google. Compatibility with manufacturer-specific Android variants may vary depending on device implementation.
For optimal security and performance, devices should run the latest available operating system updates.
## Required device permissions and configuration [#required-device-permissions-and-configuration]
Certain device permissions and user settings are required for the application to function correctly, including but not limited to the following:
* Enabling camera access: The application requires access to the device camera for credential claiming and presentation flows. If camera access is disabled, these flows will not function.
* Enabling biometrics: Some application security features rely on biometric authentication. If biometrics are not enabled on the device, these features will not be available.
* Device text size and font scaling: Extreme text size or font scaling settings may affect layout, readability, or visibility of on-screen controls. Users should configure these settings to meet their accessibility needs while still allowing the app interface to display correctly.
* Connectivity and settings: Some features require access to the public internet. Users must ensure a stable connection is available.
## Tested devices [#tested-devices]
Mobile wallet functionality relies on several device capabilities, including:
* Camera access for QR and credential exchange flows
* Secure storage and hardware-backed key protection
* Biometric authentication
* Reliable network connectivity
Variations in hardware, operating system customization, and manufacturer implementations can affect these capabilities. For this reason, MATTR verifies functionality only on the devices listed below.
Devices not listed here are not verified by MATTR and functionality or performance on those devices is not guaranteed.
### iOS [#ios]
* iPhone 15 Pro
* iPhone 15
* iPhone 14 Plus
* iPhone 14
* iPhone 12
* iPhone 11 Pro
* iPhone XR
* iPhone XS
* iPhone SE
### Android [#android]
#### Samsung [#samsung]
* Samsung Galaxy S24
* Samsung Galaxy A55
* Samsung Galaxy S23 FE
* Samsung Galaxy S22
* Samsung Galaxy S20 FE 5G
* Samsung Galaxy Note20 5G
#### Google [#google]
* Google Pixel 7 Pro
* Google Pixel 7
* Google Pixel 6 Pro
MATTR may update the list of verified devices and supported operating system versions as new devices and platform updates are released. Customers should ensure their environments remain aligned with the current requirements.
# OID4VCI Authorization Code flow journey pattern
URL: /docs/holding/credential-claiming-journey-patterns/authorization-code-journey-pattern
This journey pattern assumes that the wallet is claiming a credential from an issuer using MATTR VII. However, the same pattern can be applied to any OID4VCI-compliant issuer.
This journey pattern is used to issue credentials of different formats to a holder via the OID4VCI
[Authorization Code](/docs/issuance/authorization-code/overview) flow.
## Overview [#overview]
* **Issuance channel**: Remote, Unsupervised
* **Device/s**: On-device / Cross-device / in-person
* **Formats**: mDocs, CWT, Semantic CWT
* **Information assurance level**: High
* **Identity assurance level**: High (when identity assurance checks are included)
## Journey flow [#journey-flow]
## Architecture [#architecture]
### Scanning the QR code [#scanning-the-qr-code]
The QR code that is used to initiate the issuance workflow is created by the Issuer, but the Holder
selects when to scan it using their digital wallet (1) which triggers the issuance workflow.
This QR code can be sent to the holder via any existing communication channels, including digital
and paper based channels.
### Credential offer [#credential-offer]
Once the QR code is scanned it will result in the wallet displaying the credential offer that was
created by the Issuer using MATTR VII issuance capabilities. The offer details the credential
formats that will be issued in this workflow, and what details would each credential include.
Digital Trust Service capabilities (9) enable creating and maintaining policies that define what
Issuers can be trusted and what credential types they are allowed to issue. This introduces an
additional level of trust to interactions within the trust network, making it easier for Samantha to
decide whether or not she wishes to claim a credential from this Issuer.
### Obtaining a binding attribute [#obtaining-a-binding-attribute]
The OpenID Credential Provisioning (2) component commences the credential issuance flow by obtaining
a unique binding attribute from the requesting device/wallet. This happens when the user accepts the
credential offer (step 3 in the pattern above).
The binding attribute is carried through the proceeding steps to bind the intended credential holder
and the data.
### Authentication [#authentication]
The OpenID Connect Provider (IdP) is a component managed by the Issuer to authenticate users against
the data they hold about them. The Issuer’s IdP (3) facilitates the authentication flow (step 4 in
the pattern above) which may include multiple steps that test different factors such as:
* Basic username/password authentication.
* Proving device ownership through acknowledging a unique request sent directly to the device.
* Providing genuine presence assurance (liveness check) that the correct user is in possession of
the device being used to facilitate the journey.
The issuer can configure this authentication flow requests to include login hint parameters (for
example pre populating the user e-mail address) to create more seamless user experiences.
### Interaction hook [#interaction-hook]
Following successful authentication, the user can be redirected to a custom component (4) hosted or
controlled by the Issuer (step 5 in the pattern above). This custom component can initiate
additional steps for the user to perform as part of issuing the credential.
This can be used to embed enrolment within the issuance journey.
We refer to these custom components as *Interaction hooks*.
### Claims source [#claims-source]
As part of gathering authenticated claims that are included in the issued credential, the Issuer may
want to retrieve additional information about the user from external data store/s (5).
This optional step (step 6 in the pattern above) enables issuing credentials that are more rich in
information and thus provide greater value. It is achieved by configuring an external data store,
which we refer to as a *Claims source*.
### Credential generation [#credential-generation]
The information then gets passed back through the OpenID Credential Provisioning component (2) to
map against an established vocabulary, and express the intended context around each piece of
information it holds.
The mapped data is then passed to the Credential Generation component (6) which formats, binds and
signs the data into a credential that is ready to be sent to the requesting wallet/device (step 7 in
the pattern above).
The credential can be issued in multiple-formats. Additional features may be enabled to support
capabilities such as credential revocation or allowing the holder to respond to selective-disclosure
verification requests.
### Credential management [#credential-management]
Digital wallets (1) can be used to manage the acceptance and secure storage of the credential on the
Holder’s device upon completion of the credential issuance flow (step 8 in the pattern above). This
can be achieved by wallets built with our MATTR Pi Wallet SDK or branded MATTR GO Hold applications.
### Webhooks [#webhooks]
At the completion of the issuance flow, MATTR VII will trigger any configured Webhook events to
configured recipients (7). These events (step 9 in the pattern above) offer additional information
about the credential issuance (such as the wallet DID) back to the issuer for them to utilize or
store.
This enables integrating issuance workflows into existing business processes, or creating new ones
based on this capability.
# OID4VCI Pre-authorized Code flow journey pattern
URL: /docs/holding/credential-claiming-journey-patterns/pre-authorized-code-journey-pattern
This journey pattern assumes that the wallet is claiming a credential from an issuer using MATTR VII. However, the same pattern can be applied to any OID4VCI-compliant issuer.
This journey pattern is used to issue credentials of different formats to a holder via the OID4VCI
[Pre-authorized Code flow](/docs/issuance/pre-authorized-code/overview) protocol.
## Overview [#overview]
* **Issuance channel**: Remote, Unsupervised
* **Device/s**: On-device / Cross-device / in-person
* **Formats**: mDocs
* **Information assurance level**: High
* **Identity assurance level**: High (depends on the mechanism used by the issuer to authenticate
the holder prior to sharing a credential offer)
## Journey flow [#journey-flow]
## Architecture [#architecture]
### Logging into a provider's portal [#logging-into-a-providers-portal]
The underlying architecture assumes that the Issuer can reliably confirm the user's identity before
issuing the credential offer. This enables seamless, low-friction issuance experiences while
preserving trust and security.
### Scanning the QR code [#scanning-the-qr-code]
The QR code used to initiate the issuance workflow is generated by the Issuer, while the Holder
controls when to scan it using their digital wallet. Scanning the QR code triggers the credential
issuance process.
### Credential offer [#credential-offer]
Once the QR code is scanned it will result in the wallet displaying the credential offer that was
created by the Issuer using MATTR VII issuance capabilities. When the QR code is scanned, the
Holder’s digital wallet initiates the credential issuance workflow and displays the credential offer
prepared by the Issuer using MATTR VII's issuance capabilities.
The offer outlines the credential formats to be issued and specifies the claims included in each
credential. In a pre-authorized flow, the Issuer can gather information about the intended Holder in
advance—since the offer is created for a known user—allowing the Issuer to tailor the credential
with specific, user-relevant claims.
Digital trust service capabilities enable creating and maintaining policies that define what
Issuers can be trusted and what credential types they are allowed to issue.
This introduces an additional level of trust to interactions within the trust network, making it
easier for Samantha to decide whether or not she wishes to claim a credential from this Issuer.
### Obtaining a binding attribute [#obtaining-a-binding-attribute]
The OpenID Credential Provisioning component commences the credential issuance flow by obtaining
a unique binding attribute from the requesting device/wallet. This happens when the user accepts the
credential offer.
The binding attribute is carried through the proceeding steps to bind the intended credential holder
and the data.
### Transaction code [#transaction-code]
To enhance the security of the pre-authorized issuance flow, the Issuer may optionally require the
Holder to provide a transaction code before the credential can be claimed. This code is generated by
the Issuer as part of the credential offer and is uniquely associated with that offer.
The Issuer sends the transaction code to the user through a secure, alternative communication
channel such as email or SMS. When the user initiates the issuance process by scanning the QR code,
they are prompted by the wallet to enter the transaction code. The credential can only be issued if
the correct code is supplied.
This optional verification step strengthens the assurance that the credential is issued to the
intended recipient, helping to prevent unauthorized access in scenarios where the credential offer
may have been intercepted or misdirected.
### Credential issuance [#credential-issuance]
The information then gets passed back through the OpenID Credential Provisioning component to
map against an established vocabulary, and express the intended context around each piece of
information it holds.
The mapped data is then passed to the Credential Generation component which formats, binds and
signs the data into a credential that is ready to be sent to the requesting wallet/device.
### Credential management [#credential-management]
Digital wallets can be used to manage the acceptance and secure storage of the credential on the
Holder’s device upon completion of the credential issuance flow. This can be achieved by wallets
built with our MATTR Pi Wallet SDK or branded MATTR GO Hold applications.
# How to build an Android application that can present a credential via the DC API
URL: /docs/holding/dc-api/guide-android
Digital Credentials (DC) API support is currently offered as a tech preview.
The DC API specification itself is still under active development in the W3C
Web Incubator CG, and platform implementations continue to evolve. As such,
functionality may be limited, may not work in all scenarios, and could change
or break without prior notice as browsers and operating systems update their
implementations.
## Overview [#overview]
This guide demonstrates how to use the [Holder SDK](/docs/holding/sdk-overview) to build an
Android application that can present a claimed [mDoc](/docs/concepts/mdocs) to a verifier [remotely](/docs/verification/remote-overview) via the [DC API](/docs/holding/dc-api/overview), as per Annex D of [ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html).
## Prerequisites [#prerequisites]
### Application base [#application-base]
This guide builds on the knowledge gained in the [Claim a credential](/docs/holding/credential-claiming-tutorial) and [Remote presentation](/docs/holding/remote-presentation-tutorial) tutorials.
It is recommended to complete those tutorials first, then return here to add support for the [DC API workflow](/docs/holding/dc-api/workflow).
### Testing Devices [#testing-devices]
* Mobile device:
* Android device running Android 14 or later.
* The device must have a holder application with claimed credentials set up as per the [Claim a credential tutorial](/docs/holding/credential-claiming-tutorial) and support for the DC API enabled as per the instructions in this guide.
* Desktop device with a web browser that supports the DC API:
* Chrome or a Chromium based browser v138 or later.
* Safari v26 or later.
## How it works [#how-it-works]
The DC API is a browser standard that enables web applications to request and verify digital credentials directly from compatible wallet applications on the user's device. This provides a seamless user experience where credential presentation happens entirely within the browser context.
From a holder application point of view, there are several changes required to support remote
presentations via the DC API compared to the standard [OID4VP remote presentation workflow](/docs/verification/remote-web-verifiers/workflow):
* When the DC API feature is enabled upon initialization, all claimed credentials are automatically registered with the [Digital Credentials Manager (DCM)](https://developer.android.com/identity/digital-credentials), making them available when a website requests credentials of matching types.
* When a user selects to present a credential from the holder application, the SDK handles the entire interaction in the background by:
* Receiving the presentation request from the operating system via the DCM.
* Locating the requested credential.
* Authenticating the user via the operating system's native authentication mechanisms (fingerprint recognition, face recognition, or PIN code as defined for the credential).
* Creating and returning the credential presentation to the DCM, which then continues the interaction with the browser without the holder application needing to be directly involved in the process.
## Adjusting your mobile application to support remote presentations via the DC API [#adjusting-your-mobile-application-to-support-remote-presentations-via-the-dc-api]
The Holder SDK handles most of the complexity of the DC API workflow internally. You will have to make the following adjustments to enable it in your application:
1. Initialize the SDK with support for the DC API enabled.
2. Handle callbacks for verifier authentication (optional).
### Initialize the SDK with support for the DC API enabled [#initialize-the-sdk-with-support-for-the-dc-api-enabled]
When you initialize the SDK with the DC API feature enabled, the SDK automatically:
* Registers your application as a credential provider with the Android operating system
* Registers all claimed credentials with the Digital Credentials Manager (DCM)
* Handles incoming presentation requests
Adjust your SDK initialization code to enable DC API support by setting the `enabled` flag to `true` in the `DcmConfiguration`:
```kotlin title="Initializing the SDK with DC API support enabled"
MobileCredentialHolder.getInstance().initialize(
context,
dcmConfiguration = DcmConfiguration(enabled = true)
)
```
### Handle verifier authentication results (optional) [#handle-verifier-authentication-results-optional]
The SDK provides an API to detect if the Verifier can be trusted. This is an optional step as the SDK will handle the presentation flow and user authentication with the operating system in the background without any involvement from the app. However, if your application has specific requirements around when verifier authentication should be required, you can use this API to enforce that.
Perform the following steps to implement verifier authentication in your application:
1. Add the following
[intent filter](https://developer.android.com/guide/components/intents-filters#Receiving) in your application's `AndroidManifest.xml` file to allow it to receive the verifier authentication result from the SDK:
```xml title="AndroidManifest.xml"
```
These are both **optional** intent filters. The first one allows your application to receive the verifier authentication result when the verifier is trusted or untrusted but signed. The second one allows your application to receive a callback when an error occurs during the authentication process. If you choose not to implement these intent filters, the SDK will still handle the presentation flow and user authentication with the operating system in the background without any involvement from your app.
2. Handle the incoming intents in the corresponding activity `onCreate` method:
```kotlin title="Handling verifier confirmation and errors"
val verifierAuthenticationResult: VerifierAuthenticationResult? = when (intent.action) {
"global.mattr.credentialmanager.action.CONFIRMATION" -> {
IntentCompat.getParcelableExtra(
intent,
"global.mattr.credentialmanager.extra.verifier_authentication_result",
VerifierAuthenticationResult::class.java
)
}
"global.mattr.credentialmanager.action.ERROR" -> null
else -> {
finish()
return
}
}
```
This code example retrieves the verifier authentication result from the incoming intent when the action is `global.mattr.credentialmanager.action.CONFIRMATION`. If the action is `global.mattr.credentialmanager.action.ERROR`, it sets the result to null, indicating that an error occurred during the authentication process.
3. Handle the result of the Verifier authentication based on your requirements. For example:
```kotlin title="Handling the verifier authentication result"
when (verifierAuthenticationResult) {
is VerifierAuthenticationResult.Trusted -> {
// If this reader is trusted, just return success
setResult(RESULT_OK)
finish()
return
}
is VerifierAuthenticationResult.Untrusted, is VerifierAuthenticationResult.Unsigned -> {
// Require user confirmation and set the activity result based on the
// user's decision in the confirmation screen.
}
null -> {
// An error occurred during the authentication process. Show an error message.
// ...
setResult(RESULT_CANCELED)
finish()
return
}
}
```
In this example, if the verifier is trusted, the application simply returns a successful result. If the verifier is untrusted or unsigned, the application can prompt the user for confirmation and set the activity result based on the user's decision.
### Test the application [#test-the-application]
Let's test that the application is working as expected in both workflows.
#### Same-device workflow [#same-device-workflow]
1. Use a browser on your testing mobile device to navigate to the
[MATTR Labs remote presentation testing tool](https://tools.mattrlabs.com/verify-credentials).
2. Select **Digital Credentials API** from the *Select Experience* list.
3. Select **Request credentials**.
4. Confirm interacting with the verifier on your mobile device when prompted by the operating system (this might look different based on the device and operating system version).
5. Select the credential you wish to send to the verifier from the list of credentials suggested by the operating system.
6. Send the response.
7. The UI prompt will close, and you should see a successful verification indication in the browser where you initiated the request.
#### Cross-device workflow [#cross-device-workflow]
1. Use a desktop browser to navigate to the [MATTR Labs remote presentation testing tool](https://tools.mattrlabs.com/verify-credentials).
2. Select **Digital Credentials API** from the *Select Experience* list.
3. Select **Request credentials**.
4. Open the camera on your testing device and scan the QR code.
5. Confirm interacting with the verifier on your mobile device when prompted by the operating system (this might look different based on the device and operating system version).
6. Select the credential you wish to send to the verifier from the list of available credentials suggested by the operating system.
7. Send the response.
8. The UI prompt will close.
9. Back on your desktop browser, you should see a successful verification indication.
## Summary [#summary]
You have just used the [Android Holder SDK](/docs/holding/sdk-overview) to build an Android application
that can present a claimed [mDoc](/docs/concepts/mdocs) to a verifier
[remotely](/docs/verification/remote-overview) via the [DC API](https://wicg.github.io/digital-credentials), as per Annex D of [ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html).
This was achieved by making the following adjustments to your application:
1. Initialize the SDK with support for the DC API enabled.
2. Optionally, handle the verifier's authentication result.
## What's next? [#whats-next]
* You can [build a web application](/docs/verification/remote-web-verifiers/dc-api/guide) that will interact with your
wallet application via an online presentation workflow using the DC API.
* You can build additional capabilities into your new application:
* Present a claimed mDoc for verification via a [proximity presentation workflow](/docs/holding/proximity-presentation-tutorial).
* You can check out the [SDK reference documentation](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/index.html) for more details on the available functions and classes.
# How to build an iOS application that can present a credential via the DC API
URL: /docs/holding/dc-api/guide-ios
Digital Credentials (DC) API support is currently offered as a tech preview.
The DC API specification itself is still under active development in the W3C
Web Incubator CG, and platform implementations continue to evolve. As such,
functionality may be limited, may not work in all scenarios, and could change
or break without prior notice as browsers and operating systems update their
implementations.
## Overview [#overview]
This guide demonstrates how to use the [iOS Holder SDK](/docs/holding/sdk-overview) to build an
iOS application that can present a claimed [mDoc](/docs/concepts/mdocs) to a verifier [remotely](/docs/verification/remote-overview) via the [DC API](/docs/holding/dc-api/overview), as per Annex C of [ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html).
## Prerequisites [#prerequisites]
### Application base [#application-base]
This guide builds on the knowledge gained in the [Claim a credential](/docs/holding/credential-claiming-tutorial) and [Remote presentation](/docs/holding/remote-presentation-tutorial) tutorials.
It is recommended to complete those tutorials first, then return here to add support for the [DC API workflow](/docs/holding/dc-api/workflow).
### Testing Devices [#testing-devices]
* Mobile device:
* iOS device running iOS 26.0 or later.
* The device must have a holder application with claimed credentials set up as per the [Claim a credential tutorial](/docs/holding/credential-claiming-tutorial) and support for the DC API enabled as per the instructions in this guide.
* Desktop device with a web browser that supports the DC API:
* Chrome or a Chromium based browser v138 or later.
* Safari v26 or later.
### Development environment [#development-environment]
* Xcode 26.0.0 or later.
## How it works [#how-it-works]
The DC API is a browser standard that enables web applications to request and verify digital credentials directly from compatible wallet applications on the user's device. This provides a seamless user experience where credential presentation happens entirely within the browser context.
From a holder application point of view, there are several changes required to support remote
presentations via the DC API compared to the standard [OID4VP remote presentation workflow](/docs/verification/remote-web-verifiers/workflow):
* The holder application must register itself as a credential provider with the iOS operating system, allowing it to be automatically discovered when websites request credentials.
* Each claimed credential must be registered individually with the operating system to make it available when a website requests that specific credential type.
* When a user selects to present a credential from the holder application, the application isn't invoked directly. Instead, an app extension handles the interaction with the user, retrieving the presentation request and managing the credential presentation process.
## Adjusting your mobile application to support remote presentations via the DC API [#adjusting-your-mobile-application-to-support-remote-presentations-via-the-dc-api]
The Holder SDK handles most of the complexity of the DC API workflow internally. You will have to make the following adjustments to enable it in your application:
1. [Configure your XCode project to support the new app extension](#configure-your-xcode-project-to-support-the-new-app-extension).
2. [Create and configure an Identity Document Provider extension](#create-and-configure-an-identity-document-provider-extension).
3. [Initialize the SDK with support for the DC API enabled](#initialize-the-sdk-with-support-for-the-dc-api-enabled).
4. [Handle the presentation request](#handle-the-presentation-request).
### Configure your XCode project to support the new app extension [#configure-your-xcode-project-to-support-the-new-app-extension]
On iOS, DC API presentation requests are handled by an [app extension](https://developer.apple.com/documentation/technologyoverviews/app-extensions) rather than your main application. The app extension is a separate target within your Xcode project that provides a lightweight UI for credential selection and user consent.
First, configure your Xcode project so the main app can host an Identity Document Provider extension and share secure data with it.
**Step 1: Set the minimum deployment target**
1. In Xcode, select your app target → General.
2. Set Minimum Deployments to iOS 26.0 (or later).
**Step 2: Enable Keychain Sharing (required for SDK key access)**
1. Select your app target → Signing & Capabilities.
2. Click + Capability → add Keychain Sharing.
3. Add a Keychain Group and make sure you use the same Keychain Group for both the app and the extension. **The Keychain Group name must be the same as your app's `bundleId`.**
This is required because the extension must be able to access keys and secrets the SDK stores in the Keychain.
The Keychain Sharing group name **must match your app's bundle identifier exactly**, and **must be
the same for both the app and the extension**. If the group names differ between targets, or don't
match the app's `bundleId`, the SDK may store keys under an incorrect access group, leading to
initialization failures that are difficult to diagnose.
**Step 3: Enable an App Group entitlement (required for shared storage)**
1. In Signing & Capabilities, click + Capability → add App Groups.
2. Create an App Group identifier (for example: group.com.yourcompany.yourapp).
3. Select the same App Group for both the app and the extension.
4. This allows the app and extension to share data via the App Group container (for example, configuration and supporting state).
**Step 4: Register the app as a Digital Credentials provider**
1. In Signing & Capabilities, add the entitlement Digital Credentials API - Mobile Document Provider (This entitlement is only available to apps signed by an Apple Developer Program team. It may not appear for individual accounts).
2. Select all document types your holder will support (or select all during development).
This entitlement is required before iOS will allow your app (via its extension) to provide mobile documents using Apple’s identity document services.
### Create and configure an Identity Document Provider extension [#create-and-configure-an-identity-document-provider-extension]
Next you will create the app extension that iOS invokes to handle mobile document requests. You’ll also configure the extension’s entitlements so it can share Keychain items and App Group storage with the main app (which is essential for accessing the SDK’s keys and shared configuration).
**Step 1: Add the Identity Document Provider target in Xcode**
1. Open your project and select the project (blue icon) in the Project Navigator.
2. Under Targets, click the + button (or use File → New → Target…).
3. Select iOS in the template chooser.
4. Search for *Identity Document Provider*, choose it and click **Next**.
5. Enter the new target details (name, bundle identifier suffix, etc.), then click **Finish**.
After Xcode creates the target, it typically opens (or navigates you to) `DocumentProviderExtension.swift`. You should see a scaffold similar to:
```swift title="DocumentProviderExtension.swift"
import ExtensionKit
import IdentityDocumentServicesUI
import SwiftUI
@main
struct DocumentProviderExtension: IdentityDocumentProvider {
var body: some IdentityDocumentRequestScene {
ISO18013MobileDocumentRequestScene { context in
// Insert your view here
Text("Hello, world!")
}
}
func performRegistrationUpdates() async {
}
}
```
**Step 2: Configure Signing & Capabilities for the extension target**
Now you’ll add the same shared entitlements to the extension target so it can access the same Keychain items and App Group container as the main app.
1. Select the project (blue icon) in the Project Navigator.
2. Under Targets, select your new Identity Document Provider target (the extension).
3. Open the Signing & Capabilities tab.
4. Click + Capability → Keychain Sharing.
5. Add the same Keychain Group you configured on the main app target (this must match the app's `bundleId`).
6. Click + Capability → App Groups.
7. Select the same App Group you configured on the main app target.
These values **must match exactly** between the app and the extension. If they don’t, the extension won’t be able to read the SDK’s stored keys or shared data.
### Initialize the SDK with support for the DC API enabled [#initialize-the-sdk-with-support-for-the-dc-api-enabled]
**Do not call the SDK's initialize function from within the App Extension.** The SDK must only be
initialized from the main application target. Calling initialize from the extension can overwrite
storage keys and save them under an incorrect Keychain access group, causing initialization
failures in the main app that are difficult to recover from.
In this step, you’ll:
* Define the shared App Group identifier in code (so it can be reused consistently).
* Initialize the SDK with a `DCConfiguration`.
* Register your stored credentials with the Digital Credentials API, enabling your extension to respond to mdoc presentation requests from Safari and other supported browsers.
This is the final wiring step that connects: Main App → Shared Storage → SDK → Digital Credentials API → App Extension
**Step 1: Define the shared App Group constant**
Open `Constants.swift` in your **main application target** and add the shared App Group identifier:
```swift title="Constants.swift"
static let appGroup = "group.com.mattr.identityprovider"
```
The value **must exactly match** the App Group you configured for both the main app and the extension in the previous step.
Keeping this in `Constants.swift` ensures you avoid mismatches across targets.
**Step 2: Initialize the SDK with DC API support enabled**
Open `ContentView.swift` in your **main app target** and update the SDK initialization to include `dcConfiguration`:
```swift title="ContentView.swift"
try await mobileCredentialHolder.initialize(
userAuthenticationConfiguration: UserAuthenticationConfiguration(
userAuthenticationBehavior: .onDeviceKeyAccess
),
credentialIssuanceConfiguration: CredentialIssuanceConfiguration(
redirectUri: Constants.redirectUri,
autoTrustMobileCredentialIaca: true
),
dcConfiguration: DCConfiguration( // [!code focus]
appGroup: Constants.appGroup, // [!code focus]
supportedDocTypes: [.euav, .eudi, .jpMnc, .mDL, .photoid] // [!code focus]
)
)
```
When you initialize the SDK with the dcConfiguration parameter:
* The SDK registers all existing credentials with the DC API.
* Any future credentials issued to the holder will also be registered automatically.
* iOS recognizes your app extension as a valid provider for the configured document types.
* The extension becomes available to handle DC API presentation requests from supported browsers.
This ensures your holder integrates seamlessly with the system-level DC API framework.
**Step 3: Verify your configuration**
To confirm everything is set up correctly:
1. Build and run your app on the testing iOS device.
2. On first initialization with `dcConfiguration`, iOS will prompt you to: *Allow this app to be used for identity verification on websites*.
3. Approve the prompt.
4. Navigate to the [MATTR Labs remote presentation testing tool](https://tools.mattrlabs.com/verify-credentials) using a supported browser.
5. Select **Digital Credentials API** from the *Select Experience* list.
6. Select **Request credentials**.
If everything is configured correctly:
* The browser initiates a Digital Credentials request.
* iOS launches your Identity Document Provider extension.
* You’ll see the extension UI open (currently showing the placeholder view).
At this point, your end-to-end integration is complete. Your holder app is now registered as a Digital Credentials provider and ready to respond to verification requests.
Next, you’ll implement the logic to handle the presentation request and allow the user to select and present a credential.
### Handle the presentation request in the app extension [#handle-the-presentation-request-in-the-app-extension]
In this step, you’ll wire your Identity Document Provider extension into the Holder SDK so it can:
* Read credentials from the shared app container (via the App Group).
* Create a Digital Credentials presentation session from the system request context.
* Show a consent screen where the user selects which credential to share.
* Send the response back to the verifier.
**Step 1: Add the SDK dependency to the extension target**
To import the SDK in the extension, add `MobileCredentialHolderSDK.xcframework` as a dependency:
1. Select your project (blue icon) in the Project Navigator.
2. Under Targets, select your Identity Document Provider extension target.
3. Open General → scroll to Frameworks, Libraries, and Embedded Content.
4. Click `+` and add `MobileCredentialHolderSDK.xcframework` (or add it via Package Dependencies if your project uses Swift Package Manager).
**Step 2: Add required shared source files to the extension target**
The extension must compile with the same shared identifiers and UI helpers used by the main app.
1. If you haven’t completed the [Remote Presentation](/docs/holding/remote-presentation-tutorial) or [Proximity Presentation](/docs/holding/proximity-presentation-tutorial) tutorials, add the following file named `PresentCredentialsView.swift` to your project and make sure it’s included in the extension target membership:
```swift title="PresentCredentialsView.swift"
import MobileCredentialHolderSDK
import SwiftUI
struct PresentCredentialsView: View {
var viewModel: PresentCredentialsViewModel
@State var selectedID: String?
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 20) {
Text("Requested Documents")
.font(.headline)
.padding(.leading)
ForEach(viewModel.requestedDocuments, id: \.docType) { requestedDocument in
DocumentView(viewModel: DocumentViewModel(from: requestedDocument))
}
Text("Matched Credentials")
.font(.headline)
.padding(.leading)
ForEach(viewModel.matchedMetadata, id: \.id) { matchedMetadata in
VStack(alignment: .leading, spacing: 10) {
if let matchedCredential = viewModel.matchedMobileCredential(id: matchedMetadata.id) {
DocumentView(viewModel: DocumentViewModel(from: matchedCredential))
.padding(.vertical)
.background(selectedID == matchedMetadata.id ? Color.blue.opacity(0.2) : Color.clear)
.onTapGesture {
guard selectedID != matchedMetadata.id else {
selectedID = nil
return
}
selectedID = matchedMetadata.id
}
Button("Hide claim values") {
viewModel.matchedCredentials.removeAll(where: { $0.id == matchedMetadata.id })
}
.frame(maxWidth: .infinity, alignment: .center)
} else {
DocumentView(viewModel: DocumentViewModel(from: matchedMetadata))
.padding(.vertical)
.background(selectedID == matchedMetadata.id ? Color.blue.opacity(0.2) : Color.clear)
.onTapGesture {
guard selectedID != matchedMetadata.id else {
selectedID = nil
return
}
selectedID = matchedMetadata.id
}
Button("Show claim values") {
viewModel.getCredentialAction(matchedMetadata.id)
}
.frame(maxWidth: .infinity, alignment: .center)
}
}
}
}
}
if selectedID != nil {
Button("Send Response") {
viewModel.sendCredentialAction(selectedID!)
}
.buttonStyle(.borderedProminent)
.clipShape(Capsule())
.frame(maxWidth: .infinity, alignment: .center)
}
}
}
// MARK: PresentCredentialsViewModel
class PresentCredentialsViewModel {
@Binding var requestedDocuments: [MobileCredentialRequest]
@Binding var matchedCredentials: [MobileCredential]
@Binding var matchedMetadata: [MobileCredentialMetadata]
var getCredentialAction: (String) -> Void
var sendCredentialAction: (String) -> Void
init(
requestedDocuments: Binding<[MobileCredentialRequest]>,
matchedCredentials: Binding<[MobileCredential]>,
matchedMetadata: Binding<[MobileCredentialMetadata]>,
sendCredentialAction: @escaping (String) -> Void,
getCredentialAction: @escaping (String) -> Void
) {
self._requestedDocuments = requestedDocuments
self._matchedCredentials = matchedCredentials
self._matchedMetadata = matchedMetadata
self.sendCredentialAction = sendCredentialAction
self.getCredentialAction = getCredentialAction
}
func matchedMobileCredential(id: String) -> MobileCredential? {
matchedCredentials.first(where: { $0.id == id })
}
}
```
This view:
* Shows what the verifier requested.
* Lists credentials that match the request.
* Lets the user reveal claim values (optional) and consent by tapping **Send Response**.
The `PresentCredentialsViewModel` object is used to reference values from a credential request. It takes two closures in its initializer:
* `getCredentialAction: (String) -> Void` is used to display claim values.
* `sendCredentialAction: (String) -> Void` is used to send a credential response to the verifier
once the user selected a credential and provided consent by selecting the **Send Response**
button.
2. Select the `Constants.swift` file.
3. Use the File Inspector to enable Target Membership for your app extension.
4. Do the same for `DocumentView.swift` and `PresentCredentialsViewModel.swift` (and any related view models/types it depends on).
**Step 3: Implement the extension handler (`DocumentProviderExtension.swift`)**
1. Replace any existing code in `DocumentProviderExtension.swift` with the following code, which will be the basis for handling DC API presentation requests:
```swift title="DocumentProviderExtension.swift"
import ExtensionKit
import IdentityDocumentServicesUI
import SwiftUI
// DC API - Step 3.2: Import MobileCredentialHolderSDK
@main
struct DocumentProviderExtension: IdentityDocumentProvider {
var body: some IdentityDocumentRequestScene {
ISO18013MobileDocumentRequestScene { context in
DocumentProviderView(context: context)
}
}
/// This method is required for conformance to IdentityDocumentProvider.
/// However, the SDK manages all credential registrations automatically,
/// so no implementation is needed here.
func performRegistrationUpdates() async { }
}
struct DocumentProviderView: View {
@State private var viewModel: ExtensionViewModel
init(context: ISO18013MobileDocumentRequestContext) {
self._viewModel = State(initialValue: ExtensionViewModel(context: context))
}
var body: some View {
presentCredentialsView
// DC API - Step 3.9: Initialize the session when view appears
}
var presentCredentialsView: some View {
// DC API - Step 3.8: Display the credential selection UI
EmptyView()
}
}
@Observable
final class ExtensionViewModel {
// DC API - Step 3.3: Store a reference to the SDK
let context: ISO18013MobileDocumentRequestContext
// DC API - Step 3.5: Add properties to manage presentation state
init(context: ISO18013MobileDocumentRequestContext) {
self.context = context
// DC API - Step 3.4: Initialize the SDK for app extension
}
// DC API - Step 3.6: Create DC presentation session from context
func createDCSession() {
}
// DC API - Step 3.7: Retrieve credential from storage
@MainActor
func getCredential(id: String) {
}
}
```
2. Under `// DC API - Step 3.2: Import MobileCredentialHolderSDK`, import the SDK:
```swift title="Importing the SDK in the extension"
import MobileCredentialHolderSDK
```
This gives the extension access to `MobileCredentialHolder`, presentation sessions, and credential models.
3. Under `// DC API - Step 3.3: Store a reference to the SDK`, add a `holder` property referencing the `MobileCredentialHolder` singleton:
```swift title="Adding holder reference in the extension view model"
let holder = MobileCredentialHolder.shared
```
You’ll use this reference to initialize the SDK, create a presentation session, and fetch credentials.
4. Under `// DC API - Step 3.4: Initialize the SDK for app extension`, initialize the SDK using the shared App Group:
```swift title="Initializing the SDK in the extension"
do {
try holder.initializeAppExtension(appGroup: Constants.appGroup)
} catch {
print(error.localizedDescription)
}
```
`Constants.appGroup` must be **the same App Group** used in the main app’s `DCConfiguration`. This is what allows the extension to read the same stored credentials and supporting data.
5. Under `// DC API - Step 3.5: Add properties to manage presentation state`, add the properties used by the consent UI and the response flow:
```swift title="Adding presentation state properties to the extension view model"
var presentationSession: DCPresentationSession?
var matchedCredentials: [MobileCredential] = []
var matchedMetadata: [MobileCredentialMetadata] = []
var credentialRequest: [MobileCredentialRequest] = []
```
These values drive what the extension displays (requested data + matching credentials) and what it can send back to the verifier:
* `presentationSession` holds the active DC presentation session created from the system request.
* `matchedCredentials` are the credentials retrieved from storage that match the verifier’s request (used to show claim values in the UI).
* `matchedMetadata` is the metadata about the matched credentials (used to show the credential in the UI before revealing claim values).
* `credentialRequest` is the original request from the verifier (used to show what was requested and to create the presentation response).
6. Add a new function under `// DC API - Step 3.6: Create DC presentation session from context` to convert the system request context into a `DCPresentationSession` and extract the request details:
```swift title="Creating a DC presentation session from the system request context"
func createDCSession() {
presentationSession = try? holder.createDcPresentationSession(from: context)
matchedMetadata = presentationSession?.request
.flatMap { $0.matchedCredentialsMetadata }
.compactMap { $0 } ?? []
credentialRequest = presentationSession?.request
.compactMap { $0.credentialRequest }
.compactMap { $0 } ?? []
}
```
This extracts what the verifier requested (`credentialRequest`) and which stored credentials match it (`matchedMetadata`), then uses these values to populate the consent screen.
7. Add a new function under `// DC API - Step 3.7: Retrieve credential from storage` to load a specific credential by ID when the user wants to reveal claim values:
```swift title="Retrieving a credential from storage in the extension"
@MainActor
func getCredential(id: String) {
Task {
do {
let credential = try await holder.getCredential(credentialId: id)
matchedCredentials.append(credential)
} catch {
print(error)
}
}
}
```
8. Under `// DC API - Step 3.8: Display the credential selection UI` replace `EmptyView()` with the following code to show the `PresentCredentialsView` and pass the necessary data and actions:
```swift title="Displaying the credential selection UI in the extension"
PresentCredentialsView(
viewModel: PresentCredentialsViewModel(
requestedDocuments: $viewModel.credentialRequest,
matchedCredentials: $viewModel.matchedCredentials,
matchedMetadata: $viewModel.matchedMetadata,
sendCredentialAction: { credentialID in
Task {
try? await viewModel.presentationSession?.sendResponse(credentialIDs: [credentialID])
}
},
getCredentialAction: viewModel.getCredential(id:)
)
)
```
* `sendCredentialAction` sends the selected credential back to the relying party using DCPresentationSession.sendResponse(...).
* `getCredentialAction` loads a specific credential to display claim values before consent.
9. Under `// DC API - Step 3.9: Initialize the session when view appears`, call `createDCSession()` when the view appears to set up the presentation session and populate the UI:
```swift title="Initializing the DC presentation session when the view appears"
.task {
viewModel.createDCSession()
}
```
This ensures the session and request metadata are created as soon as the extension UI is shown, so the consent screen can render immediately.
### Test the application [#test-the-application]
Let's test that the application is working as expected in both workflows.
#### Same-device workflow [#same-device-workflow]
1. Run the app and then close it (this updates the app on your testing device).
2. Use a browser on your testing mobile device to navigate to the
[MATTR Labs remote presentation testing tool](https://tools.mattrlabs.com/verify-credentials).
3. Select **Digital Credentials API** from the *Select Experience* list.
4. Select **Request credentials**.
5. Select the credential you wish to send to the verifier from the list of available apps suggested by the operating system.
6. Send the response.
7. The application extension will close, and you should see a successful verification indication in the browser where you initiated the request.
#### Cross-device workflow [#cross-device-workflow]
1. Run the app and then close it (this updates the app on your testing device).
2. Use a desktop browser to navigate to the
[MATTR Labs remote presentation testing tool](https://tools.mattrlabs.com/verify-credentials).
3. Select **Digital Credentials API** from the *Select Experience* list.
4. Select **Request credentials**.
5. Open the camera on your testing iOS device and scan the QR code.\
(When using Safari and an iOS device with the same Apple ID, the request is automatically transferred to the mobile device).
6. Select the credential you wish to send to the verifier from the list of available apps suggested by the operating system.
7. Send the response.
8. The application extension will close.
9. Back on your desktop browser, you should see a successful verification indication.
## Summary [#summary]
You have just used the [iOS Holder SDK](/docs/holding/sdk-overview) to build an iOS application
that can present a claimed [mDoc](/docs/concepts/mdocs) to a verifier
[remotely](/docs/verification/remote-overview) via the [DC API](https://wicg.github.io/digital-credentials), as per Annex C of [ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html).
This was achieved by making the following adjustments to your application:
1. Create the app extension to handle the DC API presentation requests.
2. Initialize the SDK with support for the DC API enabled.
3. Handle the presentation request in your app extension.
## What's next? [#whats-next]
* You can [build a web application](/docs/verification/remote-web-verifiers/dc-api/guide) that will interact with your
wallet application via an online presentation workflow using the DC API.
* You can build additional capabilities into your new application:
* Present a claimed mDoc for verification via a [proximity presentation workflow](/docs/holding/proximity-presentation-tutorial).
* You can check out the [iOS Holder SDK reference documentation](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk) for more details on the available functions and
classes.
# How to build a React Native application that can present a credential via the DC API
URL: /docs/holding/dc-api/guide-react-native
Digital Credentials (DC) API support is currently offered as a tech preview.
The DC API specification itself is still under active development in the W3C
Web Incubator CG, and platform implementations continue to evolve. As such,
functionality may be limited, may not work in all scenarios, and could change
or break without prior notice as browsers and operating systems update their
implementations.
## Overview [#overview]
This guide demonstrates how to use the [Holder SDK](/docs/holding/sdk-overview) to build a
React Native application that can present a claimed [mDoc](/docs/concepts/mdocs) to a verifier
[remotely](/docs/verification/remote-overview) via the [DC API](/docs/holding/dc-api/overview).
The React Native Holder SDK wraps the native iOS and Android Holder SDKs, so the underlying
behaviour matches the [iOS guide](/docs/holding/dc-api/guide-ios) (Annex C of
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html)) and the
[Android guide](/docs/holding/dc-api/guide-android) (Annex D). The difference is that you
enable and configure the feature from a single JavaScript `initialize` call, while some
platform-specific native setup is still required, particularly on iOS.
## Prerequisites [#prerequisites]
### Application base [#application-base]
This guide builds on the knowledge gained in the [Claim a credential](/docs/holding/credential-claiming-tutorial) and [Remote presentation](/docs/holding/remote-presentation-tutorial) tutorials.
It is recommended to complete those tutorials first, then return here to add support for the [DC API workflow](/docs/holding/dc-api/workflow).
### Testing Devices [#testing-devices]
* Mobile device:
* An iOS device running iOS 26.0 or later, or an Android device running Android 14 or later.
* The device must have a holder application with claimed credentials set up as per the [Claim a credential tutorial](/docs/holding/credential-claiming-tutorial) and support for the DC API enabled as per the instructions in this guide.
* Desktop device with a web browser that supports the DC API:
* Chrome or a Chromium based browser v138 or later.
* Safari v26 or later.
### Development environment [#development-environment]
* For iOS, Xcode 26.0.0 or later is required to build the app extension described below.
## How it works [#how-it-works]
The DC API is a browser standard that enables web applications to request and verify digital credentials directly from compatible wallet applications on the user's device. This provides a seamless user experience where credential presentation happens entirely within the browser context.
From a holder application point of view, there are several changes required to support remote
presentations via the DC API compared to the standard [OID4VP remote presentation workflow](/docs/verification/remote-web-verifiers/workflow). The React Native Holder SDK exposes these through a single `dcConfiguration` option on `initialize`, which carries separate configuration for each platform:
* On **Android**, enabling the DC API is handled entirely from JavaScript. When you set the Android configuration, the SDK registers all claimed credentials with the [Digital Credentials Manager (DCM)](https://developer.android.com/identity/digital-credentials) and handles incoming presentation requests in the background.
* On **iOS**, you configure the DC API from JavaScript (the app group and supported document types), but presentation requests are served by a native [Identity Document Provider app extension](https://developer.apple.com/documentation/technologyoverviews/app-extensions). App extensions do not run in the React Native runtime, so this part must be implemented natively in Swift against the [iOS Holder SDK](/docs/holding/dc-api/guide-ios).
Configuration provided for the platform you are not currently running on is ignored, so it is safe to supply both `ios` and `android` configuration in the same call.
## Adjusting your application to support remote presentations via the DC API [#adjusting-your-application-to-support-remote-presentations-via-the-dc-api]
You will make the following adjustments to enable the DC API in your application:
1. [Enable the DC API during SDK initialization](#enable-the-dc-api-during-sdk-initialization).
2. [Complete the Android-specific setup](#complete-the-android-specific-setup).
3. [Complete the iOS-specific setup](#complete-the-ios-specific-setup).
4. [Test the application](#test-the-application).
### Enable the DC API during SDK initialization [#enable-the-dc-api-during-sdk-initialization]
Enable DC API support by passing a `dcConfiguration` object to `initialize`. The object accepts platform-specific configuration under `ios` and `android` keys:
* `android.enabled`: when `true`, all claimed credentials are enrolled with Android's Digital Credentials Manager (DCM) and made available for sharing.
* `ios.appGroup`: an [app group](https://developer.apple.com/documentation/xcode/configuring-app-groups) identifier accessible by both your app and its app extension. This must match the app group you configure in the iOS-specific setup below.
* `ios.supportedDocTypes`: the document types your holder supports, as declared in your app's entitlements. These are provided via the `SupportedDocType` enum exported by the SDK.
```ts title="Initializing the SDK with DC API support enabled"
import MobileCredentialHolder, {
SupportedDocType,
UserAuthenticationBehavior,
} from "@mattrglobal/mobile-credential-holder-react-native";
const result = await MobileCredentialHolder.initialize({
userAuthenticationConfiguration: {
userAuthenticationBehavior: UserAuthenticationBehavior.OnDeviceKeyAccess,
},
dcConfiguration: {
ios: {
appGroup: "group.com.yourcompany.yourapp",
supportedDocTypes: [
SupportedDocType.mDL,
SupportedDocType.photoid,
SupportedDocType.eudi,
SupportedDocType.euav,
SupportedDocType.jpMnc,
],
},
android: {
enabled: true,
},
},
});
if (result.isErr()) {
// Handle initialization error
console.error(result.error);
}
```
When you initialize the SDK with `dcConfiguration`:
* All existing claimed credentials are registered for the DC API on the current platform.
* Any future credentials issued to the holder are registered automatically.
* On Android, the SDK is ready to handle incoming presentation requests in the background.
* On iOS, the operating system recognizes your app extension as a valid provider for the configured document types (once the extension is set up as described below).
`initialize` must always be called from your React Native (JavaScript) code, which runs in
your main application. On iOS, **do not** attempt to initialize the SDK from within the app
extension — see the iOS-specific setup below.
### Complete the Android-specific setup [#complete-the-android-specific-setup]
On Android, setting `android.enabled` to `true` in `dcConfiguration` is all that is required to enable the DC API. The SDK registers your application as a credential provider, registers all claimed credentials with the Digital Credentials Manager, and handles incoming presentation requests and user authentication in the background — without any further involvement from your application.
#### Handle verifier authentication results (optional) [#handle-verifier-authentication-results-optional]
As with the native Android SDK, you can optionally detect whether the verifier can be trusted and require user confirmation accordingly. This is handled natively in your Android project (via intent filters in `AndroidManifest.xml` and your Android activity), not from JavaScript.
If you need this, follow the *Handle verifier authentication results* step in the [Android guide](/docs/holding/dc-api/guide-android#handle-verifier-authentication-results-optional). The intent filters, actions, and `VerifierAuthenticationResult` handling are identical for a React Native application, because they live in the native Android layer of your app.
If you do not implement this, the SDK still handles the presentation flow and user authentication with the operating system in the background.
### Complete the iOS-specific setup [#complete-the-ios-specific-setup]
On iOS, presentation requests are served by a native **Identity Document Provider app extension** rather than your main application. Because app extensions do not run the React Native runtime, this extension must be implemented natively in Swift, using the [iOS Holder SDK](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk) directly.
The JavaScript `initialize` call you set up above handles registering your credentials with the DC API. The remaining iOS work is native Xcode configuration and the extension itself, which is the same as for a native iOS application. Follow the [iOS guide](/docs/holding/dc-api/guide-ios) for the full walkthrough, and apply the React Native-specific notes below.
From the [iOS guide](/docs/holding/dc-api/guide-ios), complete the following in your app's `ios` Xcode workspace:
1. [Configure your Xcode project to support the new app extension](/docs/holding/dc-api/guide-ios#configure-your-xcode-project-to-support-the-new-app-extension) — set the deployment target, enable Keychain Sharing, add an App Group, and add the Digital Credentials provider entitlement.
2. [Create and configure an Identity Document Provider extension](/docs/holding/dc-api/guide-ios#create-and-configure-an-identity-document-provider-extension) — add the extension target and give it the same Keychain Sharing group and App Group as the main app.
3. [Handle the presentation request in the app extension](/docs/holding/dc-api/guide-ios#handle-the-presentation-request-in-the-app-extension) — implement the extension handler that creates the presentation session, shows the consent UI, and sends the response.
When applying those steps to a React Native project, note the following differences:
* **The App Group must match your JavaScript configuration.** The app group you configure on the main app target and the extension target must exactly match the `dcConfiguration.ios.appGroup` value you passed to `initialize` in your React Native code. A common approach is to define the app group once as a Swift constant for the extension and pass the same string from JavaScript.
* **Initialize the SDK from React Native, not from the extension.** Your main application initializes the SDK through the React Native `initialize` call, so you do **not** add the main-app initialization step from the iOS guide. The app extension does still call `initializeAppExtension(appGroup:)` (as shown in the iOS guide) so it can read the shared credential storage.
Do not call the SDK's `initialize` function from within the app extension. The SDK must only be
initialized from your main application — in a React Native app this is your `initialize` call in
JavaScript. The extension uses `initializeAppExtension(appGroup:)` instead. Calling `initialize`
from the extension can overwrite storage keys and save them under an incorrect Keychain access
group, causing initialization failures that are difficult to recover from.
* **Add the native iOS Holder SDK to the extension target.** The extension imports `MobileCredentialHolderSDK` (the native iOS SDK) directly, as described in the iOS guide. This is separate from the `@mattrglobal/mobile-credential-holder-react-native` package your JavaScript code uses, and is added to the extension target via the framework or Swift Package Manager.
The presentation UI and handler code inside the extension are pure Swift and identical to the iOS guide — there is no React Native equivalent, because the extension runs outside the React Native runtime.
### Test the application [#test-the-application]
Let's test that the application is working as expected in both workflows.
On iOS, run the app and then close it (this updates the app and its extension on your testing device) before testing.
#### Same-device workflow [#same-device-workflow]
1. Use a browser on your testing mobile device to navigate to the
[MATTR Labs remote presentation testing tool](https://tools.mattrlabs.com/verify-credentials).
2. Select **Digital Credentials API** from the *Select Experience* list.
3. Select **Request credentials**.
4. Confirm interacting with the verifier on your mobile device when prompted by the operating system (this might look different based on the device and operating system version).
5. Select the credential you wish to send to the verifier from the list suggested by the operating system.
6. Send the response.
7. The UI prompt will close, and you should see a successful verification indication in the browser where you initiated the request.
#### Cross-device workflow [#cross-device-workflow]
1. Use a desktop browser to navigate to the [MATTR Labs remote presentation testing tool](https://tools.mattrlabs.com/verify-credentials).
2. Select **Digital Credentials API** from the *Select Experience* list.
3. Select **Request credentials**.
4. Open the camera on your testing device and scan the QR code.\
(On iOS, when using Safari and a device with the same Apple ID, the request is automatically transferred to the mobile device.)
5. Confirm interacting with the verifier on your mobile device when prompted by the operating system (this might look different based on the device and operating system version).
6. Select the credential you wish to send to the verifier from the list suggested by the operating system.
7. Send the response.
8. The UI prompt will close.
9. Back on your desktop browser, you should see a successful verification indication.
## Summary [#summary]
You have just used the [Holder SDK](/docs/holding/sdk-overview) to build a React Native application
that can present a claimed [mDoc](/docs/concepts/mdocs) to a verifier
[remotely](/docs/verification/remote-overview) via the [DC API](https://wicg.github.io/digital-credentials).
This was achieved by making the following adjustments to your application:
1. Enable the DC API during SDK initialization by passing a `dcConfiguration`.
2. On Android, no further setup is required (verifier authentication handling is optional).
3. On iOS, create and configure a native Identity Document Provider app extension to handle presentation requests.
## What's next? [#whats-next]
* You can [build a web application](/docs/verification/remote-web-verifiers/dc-api/guide) that will interact with your
wallet application via an online presentation workflow using the DC API.
* You can build additional capabilities into your new application:
* Present a claimed mDoc for verification via a [proximity presentation workflow](/docs/holding/proximity-presentation-tutorial).
* You can check out the [React Native Holder SDK reference documentation](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/index.html) for more details on the available functions and classes.
# Remote web presentation DC API journey pattern
URL: /docs/holding/dc-api/journey-pattern
This journey pattern is used to verify an mDoc remotely via an
[online verification workflow](/docs/verification/remote-overview), as per
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) using the [DC API](/docs/verification/remote-web-verifiers/dc-api/overview).
## Overview [#overview]
* **Issuance channel**: Remote, unsupervised
* **Device/s**: Same-device / Cross-device
* **Formats**: mDocs
* **Information assurance level**: High
* **Identity assurance level**: High
## Journey flow [#journey-flow]
## Architecture [#architecture]
### Interacting with the verifier application [#interacting-with-the-verifier-application]
The user accesses a verifier web application using a supported web browser, on either a desktop or a mobile device.
### Requesting a credential for verification [#requesting-a-credential-for-verification]
Within the verifier application, the user initiates an interaction that requires presenting a mobile document (mDoc) for verification.
The verifier application embeds the MATTR Verifier Web SDK and first checks whether the user’s browser supports the Digital Credentials API (DC API). If supported, the verifier application uses the SDK to initiate a presentation session with a configured MATTR VII verifier tenant.
The request sent to the MATTR VII verifier tenant specifies:
* Which credentials are required
* Which claims from those credentials are needed for verification
The MATTR VII verifier tenant creates a new presentation session and returns a verification request object to the verifier application.
### Invoking the Digital Credentials API [#invoking-the-digital-credentials-api]
The verifier application passes the verification request object to the browser to invoke the Digital Credentials API.
Based on the user’s device and environment, the browser presents an appropriate verification interface to the user:
* On desktop devices, this may be rendered as a QR code
* On mobile devices, this may be rendered as an in-browser control (for example, a button) to start the verification directly
The user initiates the verification process by scanning the QR code or interacting with the in-browser control.
### Selecting and reviewing a credential [#selecting-and-reviewing-a-credential]
Once initiated, the browser forwards the verification request to the user’s mobile device.
The mobile operating system displays a system-managed interface listing matching credentials available from installed wallet applications that are registered to handle DC API requests. This interface is rendered by the mobile device and appears on top of the web browser.
The user selects a credential to present.
* On some platforms (for example, iOS), if only one compatible wallet holds a matching credential, the operating system may directly open that wallet without showing a selection interface.
### Invoking the wallet application [#invoking-the-wallet-application]
The wallet application authenticates the user and retrieves the verification request details, including:
* Which credential is being requested
* Which claims are required
* The relying party requesting the information
The wallet application displays the credential details to the user for review. This interface is rendered by the wallet application and is displayed on top of the web browser.
The user reviews the request and, if comfortable, provides consent to share the credential.
### Verifying the credential [#verifying-the-credential]
After consent is given, the wallet application returns an encrypted presentation response to the browser.
The browser forwards this presentation response to the verifier application, which then submits it to the MATTR VII verifier tenant.
The MATTR VII verifier tenant decrypts and verifies the presentation, performing checks to ensure:
* The credential has not been tampered with
* The credential has not been revoked or suspended
* The credential has not expired
* The credential was issued by a trusted issuer
### Displaying verification results [#displaying-verification-results]
The MATTR VII verifier tenant returns the verification results to the verifier application.
The verifier application displays the results to the user, allowing them to continue their interaction based on the outcome of the verification.
The MATTR VII verifier tenant can also be configured to return the verification result to a secure back-end service instead of the front-end, depending on implementation needs.
# Digital Credentials API
URL: /docs/holding/dc-api/overview
Description: Learn what the W3C Digital Credentials API (DC API) means for credential holders and wallet builders. How wallets register credentials with the operating system, how holders experience the unified credential picker, and how MATTR Holder SDKs and MATTR GO Hold support the DC API alongside OEM wallets.
DC API support is currently offered as a tech preview. The Digital Credentials API specification
itself is still under active development in the W3C Web Incubator CG, and platform implementations
continue to evolve. As such, functionality may be limited, may not work in all scenarios, and could
change or break without prior notice as browsers and operating systems update their implementations.
## What is the Digital Credentials API for holders? [#what-is-the-digital-credentials-api-for-holders]
The **Digital Credentials API (DC API)** is a web browser standard that lets credentials
held in any compatible wallet respond to credential requests from websites, directly
through the browser. For a holder, this means presenting a credential to a website without
having to leave the browser, choose between several wallet apps, or guess which app holds
the credential the website is asking for.
The DC API is being incubated in the
[W3C Web Incubator Community Group](https://wicg.github.io/digital-credentials/) and is
incorporated into the [ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html)
technical specification for remote verification of mDocs.
This page is written from the **holder** perspective, and is most relevant to teams
building wallets and holder experiences. For the verifier perspective, see the
[verifier DC API overview](/docs/verification/remote-web-verifiers/dc-api/overview).
## What changes for holders [#what-changes-for-holders]
Before the DC API, online credential presentation typically meant a long redirect: the
website triggered a wallet via a custom URL scheme, the holder was bounced into a wallet
app, and the wallet redirected back to the website with a response. That model created
familiar problems for holders:
* **The "NASCAR" wallet picker**: holders faced an overwhelming screen of wallet options,
similar to the multitude of sponsor logos on NASCAR vehicles.
* **Dead-ends**: holders picked a wallet that turned out not to be installed, or that did
not contain the credential the website was asking for.
* **Lost context**: redirects pulled the holder out of the website they were trying to
complete a task on, sometimes losing in-progress state.
The DC API shifts the experience from **choosing a wallet** to **choosing the right
credential**. The operating system queries every registered wallet on the device,
aggregates matching credentials, and presents them to the holder in a single, unified
picker. The holder picks the credential they want to share. The wallet that holds it
serves the request behind the scenes.
For the holder, the practical consequences are:
* A single, OS-native picker, regardless of how many wallets are on the device.
* Only credentials that match the request appear, removing guesswork.
* No need to leave the browser, which keeps the holder's flow intact.
* A familiar authentication step (platform biometrics or device passcode) before
sharing.
## How wallets participate in the DC API [#how-wallets-participate-in-the-dc-api]
For a credential to appear in the operating system's picker, the wallet that holds it must
register it with the OS as available for DC API requests. This registration happens when
the wallet stores the credential, and is refreshed as credentials are added, updated, or
removed.
At a high level, a DC API-capable wallet is responsible for:
* **Registering** credentials with the operating system so they can be discovered by DC
API requests.
* **Matching** registered credentials against incoming DC API requests so the OS can
surface only the relevant ones to the holder.
* **Authenticating the holder** at presentation time, typically using platform-level
biometrics or a device passcode.
* **Creating and signing the verifiable presentation** using the cryptographic key bound
to the credential, which remains secure in the device's key store.
* **Returning the presentation** through the DC API so the browser can deliver it back to
the verifier's session.
From the holder's point of view none of this is visible. The wallet that actually serves
the request is just the one that holds the credential the holder selected.
## How a holder experiences a DC API request [#how-a-holder-experiences-a-dc-api-request]
A DC API-based verification flow looks roughly like this from the holder's perspective.
### The website asks for a credential [#the-website-asks-for-a-credential]
While interacting with a website, the holder reaches a point where the website needs to
verify something - identity, age, entitlement. The website triggers a DC API request in
the browser.
The holder does not see a wallet selection screen. They stay in the website's context.
### The operating system shows matching credentials [#the-operating-system-shows-matching-credentials]
The operating system looks across every wallet that has registered credentials and finds
the ones that match the request. It then shows the holder a unified picker that includes
only those matching credentials.
If only one credential matches, some platforms may skip directly to the next step.
### The holder authenticates and consents [#the-holder-authenticates-and-consents]
The holder selects the credential they want to share. The operating system asks the holder
to authenticate using platform biometrics or a device passcode, and shows what information
will be shared with the verifier.
If the holder cancels, the website is returned to its original state without losing
context.
### The wallet creates and shares the presentation [#the-wallet-creates-and-shares-the-presentation]
Behind the scenes, the wallet that holds the selected credential creates and signs a
verifiable presentation using the credential's private key, which never leaves the device.
The presentation is returned through the DC API to the website.
The holder remains in the browser throughout. There is no app switch and no return
redirect.
## Same-device and cross-device flows for holders [#same-device-and-cross-device-flows-for-holders]
The DC API supports two interaction patterns:
* **Same-device flows**: The holder is using the website on the same mobile device that
holds their credentials. The DC API surfaces matching credentials directly, and the
exchange happens entirely within the browser on that device.
* **Cross-device flows**: The holder is using the website on a different device, typically
a desktop or laptop. The desktop browser displays a QR code (or similar mechanism). The
holder scans it with their mobile device, which then invokes the DC API on the mobile
platform. Matching credentials are presented on the mobile device. The holder
authenticates and consents on the mobile device, and the presentation is delivered back
to the desktop browser to continue the task.
Cross-device flows use operating-system-level proximity checks (such as CTAP 2.1 over
Bluetooth Low Energy) to confirm the two devices are physically close together before the
exchange proceeds. This protects against attacks where someone tries to use a screenshot
of a QR code or hijack a session from afar.
## Security and privacy properties for holders [#security-and-privacy-properties-for-holders]
The DC API is designed with several properties that directly benefit holders:
* **No silent tracking**: The DC API prevents unauthorized apps from silently tracking
credential interactions. Sharing requires explicit holder action.
* **Platform-level authentication**: The holder authenticates using the same biometric or
passcode mechanism they already use to unlock the device. The wallet does not need to
re-implement authentication primitives, which reduces the chance of weaker custom
implementations.
* **Keys stay on the device**: The private keys associated with credentials remain in the
device's secure key store. Presentations are signed locally rather than by handing keys
to a remote service.
* **Selective disclosure**: When the underlying credential format supports it (such as
mDocs), the holder can share only the specific claims the website asks for, rather than
the whole credential.
* **Session continuity**: If the holder cancels, the website returns to its original
state. The holder is not left in an ambiguous redirect state.
* **Proximity protection in cross-device flows**: The browser and operating system enforce
proximity checks for cross-device flows, which reduces the risk of remote QR-code based
attacks.
## Platform support [#platform-support]
The DC API is a web browser standard and is implemented differently across platforms:
* **iOS**: Supported on Safari version 26 and later, available on iPhone 11 and later
running iOS 26.
* **Android**: Supported on Chrome and Chromium-based browsers version 138 and later.
The DC API is specifically designed for web applications running in browsers. For native
holder apps, the platform-specific APIs are used instead, for example Android's
DigitalCredential API, which is part of the Credential Manager framework. ISO/IEC 18013-7
defines two browser-level mechanisms that are relevant here:
* **Annex C**: mDoc device retrieval over the DC API, currently supported on iOS and
Safari.
* **Annex D** (draft): OpenID4VP over the DC API, expected on Android and Chromium-based
browsers.
A wallet that supports both annexes provides a consistent holder experience across
platforms and browser types.
## How MATTR supports the DC API on the holder side [#how-mattr-supports-the-dc-api-on-the-holder-side]
MATTR provides two main paths for building holder experiences that work with the DC API:
* **[MATTR Pi Holder SDKs](/docs/holding/sdk-overview)**: Build your own wallet app, or
add credential holding capabilities to an existing app. React Native, iOS, and Android
SDKs are available, and integrate with the DC API where the platform supports it. The
SDKs handle credential registration with the operating system, authentication, and
presentation signing.
* **[MATTR GO Hold](/docs/holding/go-hold/getting-started)**: A white-label wallet
application that already includes DC API support. Apply your own branding without
writing wallet code yourself.
In both cases, credentials stored in the wallet are registered with the operating system
so they are discoverable through DC API requests, alongside other wallets on the device
including OEM wallets. The holder sees a single picker and chooses the credential they
want to share.
For platform-specific holder integration steps, see the
[iOS guide](/docs/holding/dc-api/guide-ios),
[Android guide](/docs/holding/dc-api/guide-android), and
[React Native guide](/docs/holding/dc-api/guide-react-native).
## How OEM wallets fit into the holder ecosystem [#how-oem-wallets-fit-into-the-holder-ecosystem]
A growing share of high-assurance identity credentials, such as mobile driver's licenses
and national identification cards, are issued into **OEM wallets** - the credential
wallets shipped natively on iOS and Android by Apple and Google. From a holder's
perspective, the DC API treats OEM wallets the same as any other compatible wallet on the
device. They register their credentials with the operating system, and those credentials
appear in the same unified picker.
The practical consequence for holders is that a credential issued into an OEM wallet can
be presented to any website that supports the DC API, side by side with credentials held
in third-party or MATTR-built wallets. Holders do not have to install additional apps to
use OEM-held credentials, and do not have to remember which wallet a particular credential
lives in.
For wallet builders and ecosystem partners thinking about how OEM wallets fit alongside
their own holder experience, [contact us](mailto:dev-support@mattr.global) to talk
through the design.
## Frequently asked questions [#frequently-asked-questions]
### What is the Digital Credentials API (DC API) from a holder's perspective? [#what-is-the-digital-credentials-api-dc-api-from-a-holders-perspective]
The **Digital Credentials API (DC API)** is a browser standard that lets credentials in
any wallet on a device respond to requests from websites without redirecting the holder
out of the browser. For holders, the practical effect is a single, operating-system-level
prompt that shows credentials matching what the website is asking for, regardless of which
wallet they live in.
### How does a wallet participate in the DC API? [#how-does-a-wallet-participate-in-the-dc-api]
A wallet that supports the DC API **registers the credentials it holds with the operating
system**. When a website triggers a DC API request, the operating system queries every
registered wallet, aggregates the matching credentials, and presents them to the holder in
a unified picker. When the holder selects a credential, the originating wallet creates and
signs the presentation.
### How is the DC API different from redirect-based wallet flows for holders? [#how-is-the-dc-api-different-from-redirect-based-wallet-flows-for-holders]
In a redirect-based flow, the holder is bounced out of the website into a wallet app and
then back, often after first having to pick a wallet from a long list. With the DC API,
the holder **stays in the browser**. The operating system surfaces only the credentials
that match the request, and the holder picks the **credential**, not the wallet. This
tends to be faster, less confusing, and less prone to dead-ends.
### Do MATTR-built wallets support the DC API? [#do-mattr-built-wallets-support-the-dc-api]
Yes. MATTR-built wallets, including holder applications built with the **MATTR Pi Holder
SDKs** and **MATTR GO Hold**, register the credentials they store with the operating
system so they are discoverable through DC API requests. This lets MATTR-built wallets sit
alongside other wallets, including OEM wallets, in the operating system's unified
credential picker.
### How do OEM wallets fit into the DC API ecosystem? [#how-do-oem-wallets-fit-into-the-dc-api-ecosystem]
**OEM wallets** are credential wallets shipped natively by device manufacturers such as
Apple and Google. Because OEM wallets register credentials with the operating system in
the same way other DC API-compatible wallets do, holders can present credentials from OEM
wallets and third-party wallets through the same browser experience. This expands the set
of credentials a holder can use for online verification without having to install
additional apps.
### Which browsers and platforms currently support the DC API for holders? [#which-browsers-and-platforms-currently-support-the-dc-api-for-holders]
The DC API is supported on **iOS 26 and later with Safari 26** (on iPhone 11 and later),
and on **Chrome and Chromium-based browsers version 138 and later** on Android. Holders
need a wallet that has registered credentials with the operating system, and a browser and
OS version that support the DC API.
# Remote web presentation DC API workflow
URL: /docs/holding/dc-api/workflow
DC API support is currently offered as a tech preview. The Digital Credentials API specification
itself is still under active development in the W3C Web Incubator CG, and platform implementations
continue to evolve. As such, functionality may be limited, may not work in all scenarios, and could
change or break without prior notice as browsers and operating systems update their implementations.
mDocs are digital credentials based on the ISO/IEC
[18013-5](https://www.iso.org/standard/69084.html) standard and
[18013-7](https://www.iso.org/standard/91154.html) technical specification, designed to be stored on
a holder’s mobile device and support either in-person or remote
verification workflows.
The purpose of this page is to describe the end-to-end [remote web app verification](/docs/verification/remote-overview) DC API workflow, where a
user interacts with a web application to present an mDoc stored on their mobile device via the DC API, as per Annex C and D of ISO/IEC 18013-7:2025.
## Prerequisites [#prerequisites]
We recommend you make yourself familiar with the following concepts to support your understanding of
the implementation described in this page:
* What is [credential verification](/docs/verification)?
* What are [mDocs](/docs/concepts/mdocs)?
* What is [remote verification](/docs/verification/remote-overview)?
* What is the [DC API](/docs/verification/remote-web-verifiers/dc-api/overview)?
## Overview [#overview]
The DC API is a browser standard that enables web applications to request and verify
digital credentials directly from compatible wallet applications on the user's device. This provides a
seamless user experience within a platform-managed flow initiated from the browser. The API allows wallets to register as credential
providers with the mobile device's operating system, making them automatically discoverable when a website requests credentials.
## Detailed workflow [#detailed-workflow]
Let's take a closer look at the end-to-end workflow and explain the role of each component:
### Invoking the interaction [#invoking-the-interaction]
The user triggers an action in a verifier web application that requires presenting a credential for verification.
### Generating challenge [#generating-challenge]
When the verifier web application has a backend, the backend generates a unique challenge to ensure the security of the verification workflow. This challenge will be used to validate that the verification results received later are associated with this specific session.
Generating the challenge on the backend helps prevent tampering and replay attacks, as the challenge is not exposed to the frontend until verification is complete.
Furthermore, it enables the backend to associate the verification results with an existing session or transaction, enhancing the overall security and integrity of the verification process.
### Passing challenge to web application [#passing-challenge-to-web-application]
The verifier backend sends the generated challenge to the verifier web application, which will include it when starting the presentation session with MATTR VII.
### Checking browser support [#checking-browser-support]
The verifier web application checks if the user's browser supports the DC API.
### Requesting credentials [#requesting-credentials]
The **verifier web application** calls the SDK's `requestCredentials` method to start a presentation session with the configured **MATTR VII tenant**, including the challenge received from the backend.
### Creating presentation session [#creating-presentation-session]
The **MATTR VII tenant** creates a new presentation session and returns the request object to the **verifier web application**.
### Invoking DC API [#invoking-dc-api]
The **verifier web application** passes the request object to the user's **browser** to invoke the Digital Credentials API.
### Presenting verification request [#presenting-verification-request]
The **browser** presents a verification request interface to the **user**. This can be a QR code (when the user is on a desktop) or a button to start the process directly in the browser (when the user is on a mobile device).
### Initiating verification [#initiating-verification]
The **user** initiates the verification process by scanning the QR code/clicking the button.
### Forwarding request to mobile device [#forwarding-request-to-mobile-device]
The **browser** forwards the request to the **mobile device**.
### Displaying matching credentials [#displaying-matching-credentials]
The **mobile device** displays to the **user** matching credentials from installed **wallet applications** that are registered to handle DC API requests. This UI is rendered by the **mobile device** and is displayed on top of the web browser.
### Selecting credential [#selecting-credential]
The **user** selects a credential from the displayed options.
### Forwarding to wallet [#forwarding-to-wallet]
The **mobile device** forwards the verification request to the **wallet application** that holds the selected credential. On iOS devices, if there is only one compatible wallet that holds a matching credential, the system may directly open that wallet without displaying the selection UI to the user.
### Reviewing credential details [#reviewing-credential-details]
The mobile device displays the credential details to the **user** for review and consent. This UI is rendered by the **wallet application** and is displayed on top of the web browser in iOS, while on Android it is displayed within the system's native UI.
### Providing consent [#providing-consent]
The **user** reviews the credential details and provides consent to share them with the verifier.
### Returning presentation response [#returning-presentation-response]
The **wallet application** returns the encrypted credential as a presentation response to the **browser**.
### Forwarding to Verifier [#forwarding-to-verifier]
The **browser** forwards the presentation response to the **verifier web application**.
### Submitting for verification [#submitting-for-verification]
The **verifier web application** submits the presentation response to the **MATTR VII tenant** for verification.
### Verifying credential [#verifying-credential]
The **MATTR VII tenant** decrypts and verifies the credential.
### Notifying verification completion [#notifying-verification-completion]
The **MATTR VII tenant** notifies the **verifier web application** that verification has been completed. When the web application has a backend, the tenant does not send the verification results directly to the frontend for enhanced security.
### Passing session ID to backend [#passing-session-id-to-backend]
The **verifier web application** sends the presentation session ID to the **verifier backend** to retrieve the verification results securely.
### Retrieving verification results [#retrieving-verification-results]
The **verifier backend** makes a [request](/docs/verification/remote-verification-api-reference/presentation-sessions#retrieve-a-presentation-session-result) to **MATTR VII** to retrieve the verification results for the session using the session ID.
### Returning results with challenge [#returning-results-with-challenge]
The **MATTR VII tenant** responds with the verification results and the unique challenge that was included when the presentation session was created.
### Validating challenge [#validating-challenge]
The **verifier backend** compares the original challenge it generated with the challenge received from MATTR VII to ensure the response can be trusted and is associated with the correct session.
### Passing results to web application [#passing-results-to-web-application]
Once the challenge is validated, the **verifier backend** sends the verification results to the **verifier web application**.
### Displaying results [#displaying-results]
The **verifier web application** displays the verification results and the **user** continues the interaction accordingly.
Based on the unique challenge, the verifier can also continue any backend processes associated with the verification session, such as granting access to a service or completing a transaction.
# NFC device engagement for proximity presentations
URL: /docs/holding/proximity-presentation-guides/nfc-engagement-guide
## Overview [#overview]
This guide shows how to configure the Holder SDK to start a proximity presentation session using device engagement over NFC.
NFC-based device engagement is a convenient alternative to QR-code engagement: it is faster, simpler for users, and fully handled by the SDK.
It also simplifies your application because you do not need to generate a QR code or process engagement data manually.
## Prerequisites [#prerequisites]
This guide builds on knowledge obtained in the [Proximity Presentation tutorial](/docs/holding/proximity-presentation-tutorial). It is recommended to complete that tutorial first, then return here to learn how to configure NFC device engagement.
## Understanding NFC Device Engagement [#understanding-nfc-device-engagement]
**Device engagement** is the initial stage of a [proximity presentation session](/docs/verification/in-person-overview#engagement-phase) defined in ISO/IEC 18013-5:2021.
During engagement, the Holder sends the information required to establish a secure connection to the Verifier.
This information contains cipher suite, ephemeral device key, and channel parameters, as well as its preferred BLE role and the associated connection details.
In the [Proximity Presentation tutorial](/docs/holding/proximity-presentation-tutorial) this data was encoded into a QR code that the Verifier scanned to establish the channel and encryption.
With NFC the Verifier reads the engagement data directly from the Holder device, with no QR code generation, scanning or handling required.
NFC-based engagement supports two modes (as per ISO/IEC 18013-5:2021):
* *Static Handover*: The Holder sends its connection details to the Verifier.
* *Negotiated Handover*: The Verifier sends its supported data transfer options, then the Holder selects one and returns its connection details.
A key advantage of NFC-based engagement is that the user can start it without first opening the app. Similar to NFC payments, the user brings the device near the Verifier's device and the Holder app launches automatically. If multiple NFC-capable Holder apps are installed, the system prompts the user to choose one. A default app can be set in the device's system settings.
When multiple NFC-capable wallet apps are installed on a device, tapping the device against an NFC verifier may trigger multiple system prompts asking the user to select which app to use for the NFC interaction.
While these selection prompts are expected as part of the Android system behavior, they currently appear more than once during a single engagement, which may cause confusion for end users. The prompt will disappear if the user presses **Back**.
## Configuring NFC-based Engagement [#configuring-nfc-based-engagement]
Configuration involves the following steps:
1. Register the NFC-based engagement listener: set up a listener to receive NFC engagement events.
2. Handle device engagement and create a proximity presentation session: respond to engagement events by creating a presentation session.
### Step 1: Register the NFC-based engagement listener [#step-1-register-the-nfc-based-engagement-listener]
After you include the SDK, your Holder app becomes NFC-capable and will be launched (or suggested) when near an NFC-enabled verifier. The SDK automatically performs the engagement, but you must register a listener to receive events. Do this as early as possible in the app lifecycle.
NFC device engagement is only supported on Android devices. On iOS, the SDK returns a `PlatformNotSupported` error. If you are building a cross-platform React Native app, ensure NFC engagement code only runs on Android builds by using a `Platform.OS` check.
Coming soon...
Register the listener in `onCreate`:
```kotlin title="HolderApplication.kt"
override fun onCreate() {
super.onCreate()
MobileCredentialHolder.Nfc.setDeviceEngagementListener { error ->
if (error == null) {
// Create the session and show consent UI (e.g. start an Activity)
} else {
Log.e("HolderApplication", "NFC engagement error: ${error.message}")
}
}
}
```
Register the listener as early as possible, such as in your app's root component or entry point. Use a `Platform.OS` check to ensure this only runs on Android:
```ts title="App.tsx"
import { Platform } from "react-native";
import { setDeviceEngagementListener } from "@mattrglobal/mobile-credential-holder-react-native";
const setupNfcEngagement = async () => {
if (Platform.OS !== "android") {
return;
}
const result = await setDeviceEngagementListener((error) => {
if (error) {
handleError(error);
return;
}
// Engagement successful, create the session and show consent UI
});
if (result.isErr()) {
console.error("Failed to set NFC listener:", result.error);
}
};
```
### Step 2: Handle device engagement [#step-2-handle-device-engagement]
Start a proximity presentation session (the same way as you do [for QR-based device engagement](/docs/holding/proximity-presentation-tutorial#step-1-create-a-qr-code-for-the-verifier-to-scan)) while setting `engagementFromNfc = true` to indicate NFC was used:
Coming soon...
```kotlin
val mdocHolder = MobileCredentialHolder.getInstance()
// Minimal initialization logic
// However, if you support multiple instances, you probably want to add more robust initialization logic
if (!mdocHolder.initialized) mdocHolder.initialize(activity)
mdocHolder.createProximityPresentationSession(
activity,
onRequestReceived = { _, requests, e ->
// Show consent UI for requests or handle error
},
engagementFromNfc = true
)
```
```ts
import {
createProximityPresentationSession,
isInitialized,
initialize,
} from "@mattrglobal/mobile-credential-holder-react-native";
// Ensure the SDK is initialized
const initialized = await isInitialized();
if (!initialized) {
await initialize();
}
const result = await createProximityPresentationSession({
onRequestReceived: (requests, error) => {
if (error) {
// Handle error
return;
}
// Show consent UI for the received requests
},
engagementFromNfc: true,
});
if (result.isErr()) {
console.error("Failed to create session:", result.error);
}
```
* Your consent UI might appear when the app is already running or as the first screen after launch.
* Ensure the SDK is initialized before creating a presentation session.
## Optional steps [#optional-steps]
### Configure branding [#configure-branding]
Help users recognize your app in system prompts and settings by overriding NFC engagement resources:
Coming soon...
1. Set a description string in `res/values/strings.xml`:
```xml title="res/values/strings.xml"
Your application name.
```
This string will appear in the system prompt and in device settings for non-payment NFC services.
2. Add a drawable resource named `mattr_nfc_engagement_icon` containing your app icon.
The icon will be displayed by the system along with the description.
Branding configuration is managed through native Android resources. In your React Native project's `android` directory:
1. Set a description string in `android/app/src/main/res/values/strings.xml`:
```xml title="android/app/src/main/res/values/strings.xml"
Your application name.
```
This string will appear in the system prompt and in device settings for non-payment NFC services.
2. Add a drawable resource named `mattr_nfc_engagement_icon` containing your app icon to `android/app/src/main/res/drawable/`.
The icon will be displayed by the system along with the description.
### Set preferred BLE role and log level [#set-preferred-ble-role-and-log-level]
Set optional NFC configuration options that apply to subsequent NFC engagements, regardless of SDK initialization.
Calling this API does not require the SDK to be initialized.
Coming soon...
```kotlin
MobileCredentialHolder.Nfc.setNfcConfiguration(
context,
NfcConfiguration(
handoverMode = NfcHandoverMode.Negotiated,
bleMode = BleMode.MDocClientCentral, // Used only for Static Handover to set preferred BLE role
logLevel = LogLevel.ERROR // Log level if SDK not initialized yet
)
)
```
```ts
import { Platform } from "react-native";
import {
setNfcConfiguration,
BleMode,
NfcHandoverMode,
LogLevel,
} from "@mattrglobal/mobile-credential-holder-react-native";
if (Platform.OS === "android") {
const result = await setNfcConfiguration({
handoverMode: NfcHandoverMode.Negotiated,
bleMode: BleMode.MDocClientCentral, // Used only for Static Handover to set preferred BLE role
logLevel: LogLevel.Error, // Log level if SDK not initialized yet
});
if (result.isErr()) {
console.error("Failed to set NFC configuration:", result.error);
}
}
```
## Testing NFC-based Engagement [#testing-nfc-based-engagement]
To test NFC-based engagement:
1. Claim a credential in a holder app.
2. Use an NFC-capable verifier device and request to verify the credential claimed in the previous step.
3. Bring the devices close to each other to establish NFC connection.
4. Confirm the Holder app launches (or is suggested if multiple NFC-capable apps exist).\
If multiple NFC-capable apps exist, select yours.
5. Approve the verification request.
6. Confirm the credential is presented successfully to the verifier.
# Handling verifier authentication
URL: /docs/holding/proximity-presentation-guides/verifier-authentication
Description: Inspect the verifier authentication result during a proximity presentation so users can decide whether to share credentials with an unauthenticated verifier
## Overview [#overview]
When a verifier requests credentials during a proximity presentation, your holder application can inspect whether the verifier proved its identity before any credential data is shared. This lets you show the user who is asking for their data and how trustworthy the request is, and lets the user decline to share with a verifier that cannot be authenticated.
In a proximity presentation, the verifier can sign its request. The Holder SDK validates that signature against the trusted verifier root certificates configured on the device and exposes the outcome as a verifier authentication result on each request. Your application reads this result and decides how to proceed.
This is the mechanism that ISO/IEC 18013-5:2021 calls *mDoc Reader Authentication* (section 9.1.4). The verifier (the "mdoc reader") signs the request, and the holder ("mdoc") authenticates the reader before responding. The SDK surfaces it under the more general name *verifier authentication*, so the same concept is consistent with the remote presentation flow.
This guide covers proximity (ISO/IEC 18013-5) verifier authentication, where the verifier signs the device request and the holder validates it offline. Remote (OpenID4VP / ISO/IEC 18013-7) presentations authenticate the verifier through the signed authorization request instead. See [Handling verifier authentication for remote presentations](/docs/holding/remote-presentation-guides/verifier-authentication) for that flow.
## Prerequisites [#prerequisites]
This guide builds on the [Proximity Presentation tutorial](/docs/holding/proximity-presentation-tutorial). Complete that tutorial first so you have a working holder application that can create a proximity presentation session and receive requests, then return here to add verifier authentication handling.
Trust evaluation is performed against the trusted verifier root certificates configured on the holder device. Whether a request is reported as trusted depends on those certificates being present. For background on verifier certificates, see [Certificates overview](/docs/verification/certificates/overview).
## Understanding the verifier authentication result [#understanding-the-verifier-authentication-result]
When the SDK receives a device request, it evaluates any reader authentication it contains and attaches a verifier authentication result to each requested document. The result is one of three outcomes:
* **Trusted**: The request was signed and the signature chained to a trusted verifier root certificate. The result carries a `VerifierInfo` value describing the trusted root that was matched (its common name and the identifier of the stored certificate).
* **Untrusted**: A reader signature was present but could not be trusted. The result carries the X.509 certificate chain from the request (as PEM strings) and an error describing the first validation failure detected.
* **Unsigned**: The request carried no reader authentication. The result optionally carries the request origin if one is available.
An untrusted result reports one of the following reasons. These map directly to the requirements in ISO/IEC 18013-5:2021 section 9.1.4:
| Reason | Meaning |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Certificate not found | The verifier authentication object is missing a verifier certificate (section 9.1.4.4). |
| Invalid verifier certificate | The verifier certificate in the authentication object is invalid. |
| Inactive certificate | The certificate's `validFrom` date is in the future. |
| Expired certificate | The certificate's `validUntil` date is in the past. |
| Failed to evaluate trust | The certificate chain or the integrity of the verifier authentication object could not be verified, for example because it does not chain to a trusted root. |
## Adding trusted verifier certificates [#adding-trusted-verifier-certificates]
A request is reported as trusted only when the verifier's signature chains to a trusted verifier root certificate stored on the device. Your application provisions these certificates through the SDK. The same trusted verifier certificate store is used for both proximity and [remote](/docs/holding/remote-presentation-guides/verifier-authentication) presentations.
Certificates are supplied as PEM-encoded or Base64-encoded DER strings and conform to the IACA profile defined in ISO/IEC 18013-5. Adding a certificate is idempotent: adding the same certificate again returns the identifier of the existing entry rather than creating a duplicate.
Use [`addTrustedVerifierCertificates`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/addtrustedverifiercertificates\(certificates:\)), [`getTrustedVerifierCertificates`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/gettrustedverifiercertificates\(\)), and [`deleteTrustedVerifierCertificate`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/deletetrustedverifiercertificate\(certificateid:\)):
```swift title="CertificateSettings.swift"
let mobileCredentialHolder = MobileCredentialHolder.shared
// Add one or more trusted verifier certificates (PEM or Base64 DER).
let addedIds = try mobileCredentialHolder.addTrustedVerifierCertificates(
certificates: [verifierCertificatePem]
)
// List the stored trusted verifier certificates.
let stored: [TrustedCertificate] = try mobileCredentialHolder.getTrustedVerifierCertificates()
// Remove one by its identifier.
try mobileCredentialHolder.deleteTrustedVerifierCertificate(certificateId: addedIds[0])
```
Each stored certificate is a [`TrustedCertificate`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/trustedcertificate) carrying its `id`, `pem`, and `commonName`.
Use [`addTrustedVerifierCertificates`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/add-trusted-verifier-certificates.html), [`getTrustedVerifierCertificates`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/get-trusted-verifier-certificates.html), and [`deleteTrustedVerifierCertificate`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/delete-trusted-verifier-certificate.html):
```kotlin title="CertificateSettings.kt"
val mobileCredentialHolder = MobileCredentialHolder.getInstance()
// Add one or more trusted verifier certificates (PEM or Base64 DER).
val addedIds: List = mobileCredentialHolder.addTrustedVerifierCertificates(
listOf(verifierCertificatePem)
)
// List the stored trusted verifier certificates.
val stored: List = mobileCredentialHolder.getTrustedVerifierCertificates()
// Remove one by its identifier.
mobileCredentialHolder.deleteTrustedVerifierCertificate(addedIds.first())
```
Each stored certificate is a `TrustedCertificate` carrying its `id`, `pem`, and `commonName`.
Use [`addTrustedVerifierCertificates`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/addTrustedVerifierCertificates.html), [`getTrustedVerifierCertificates`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/getTrustedVerifierCertificates.html), and [`deleteTrustedVerifierCertificate`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/deleteTrustedVerifierCertificate.html):
```ts title="trustedVerifierCertificates.ts"
import {
addTrustedVerifierCertificates,
getTrustedVerifierCertificates,
deleteTrustedVerifierCertificate,
} from "@mattrglobal/mobile-credential-holder-react-native";
// Add one or more trusted verifier certificates (PEM or Base64 DER).
const result = await addTrustedVerifierCertificates([verifierCertificatePem]);
if (result.isErr()) {
console.error("Failed to add trusted verifier certificates:", result.error);
return;
}
const addedIds = result.value;
// List the stored trusted verifier certificates.
const stored = await getTrustedVerifierCertificates();
// Remove one by its identifier.
await deleteTrustedVerifierCertificate(addedIds[0]);
```
Each stored certificate is a [`TrustedVerifierCertificate`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/types/TrustedVerifierCertificate.html) carrying its `id`, `pem`, and `commonName`.
## Inspecting the result [#inspecting-the-result]
Each requested document delivered to your request handler carries its verifier authentication result. Read it before presenting the consent UI so you can reflect the verifier's status to the user. The session is created exactly as in the [Proximity Presentation tutorial](/docs/holding/proximity-presentation-tutorial#step-2-handle-the-presentation-request); the examples below focus on inspecting the result inside the request handler.
On iOS, each entry delivered to your `onRequestReceived` handler is a `MobileCredentialRequest`. Read its [`verifierAuthenticationResult`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialrequest) property and switch over the [`VerifierAuthenticationResult`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/verifierauthenticationresult) cases:
```swift title="ViewModel.swift"
func onRequestReceived(
_ mobileCredentialRequests: [(request: MobileCredentialRequest, matchedMobileCredentials: [MobileCredentialMetadata])]?,
error: Error?
) {
guard let mobileCredentialRequests else {
// Handle error
return
}
for (request, _) in mobileCredentialRequests {
switch request.verifierAuthenticationResult {
case .trusted(let verifierInfo):
// The request was signed by a trusted verifier root certificate.
print("Trusted verifier: \(verifierInfo.commonName)")
case .untrusted(let x509CertChain, let error):
// A reader signature was present but could not be trusted.
print("Untrusted verifier: \(error.localizedDescription)")
print("Presented chain: \(x509CertChain)")
case .unsigned(let origin):
// No reader authentication was provided.
print("Unsigned request from: \(origin ?? "unknown origin")")
}
}
}
```
See [`VerifierInfo`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/verifierinfo) and [`VerifierAuthenticationError`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/verifierauthenticationerror) for the associated values.
On Android, each item delivered to your `onRequestReceived` callback is a `CredentialRequestInfo` whose `request` property is a `MobileCredentialRequest`. Read its `verifierAuthenticationResult` and match over the [`VerifierAuthenticationResult`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.issuance.dto/-verifier-authentication-result/index.html) subtypes:
```kotlin title="ProximityPresentationViewModel.kt"
session = MobileCredentialHolder.getInstance().createProximityPresentationSession(
activity,
onRequestReceived = { _, requests, error ->
if (error != null) {
// Handle error
return@createProximityPresentationSession
}
requests?.forEach { requestInfo ->
when (val result = requestInfo.request.verifierAuthenticationResult) {
is VerifierAuthenticationResult.Trusted ->
// The request was signed by a trusted verifier root certificate.
Log.i(TAG, "Trusted verifier: ${result.verifierInfo.commonName}")
is VerifierAuthenticationResult.Untrusted ->
// A reader signature was present but could not be trusted.
Log.w(TAG, "Untrusted verifier: ${result.error.message}")
is VerifierAuthenticationResult.Unsigned ->
// No reader authentication was provided.
Log.i(TAG, "Unsigned request from: ${result.origin ?: "unknown origin"}")
}
}
}
)
```
See [`VerifierInfo`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.issuance.dto/-verifier-info/index.html) and [`VerifierAuthenticationFailureType`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.issuance.dto/-verifier-authentication-failure-type/index.html) for the associated values.
On React Native, the request handler receives a single `data` argument. When the request succeeds, `data.request` is an array of entries whose `request` property is a `MobileCredentialRequest`. Read its `verifierAuthenticationResult` and switch on the `result` discriminator of [`VerifierAuthenticationResult`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/types/VerifierAuthenticationResult.html):
```ts title="usePresentations.ts"
const result = await createProximityPresentationSession({
onRequestReceived: (data) => {
if ("error" in data) {
// Handle error
return;
}
data.request.forEach(({ request }) => {
const auth = request.verifierAuthenticationResult;
switch (auth.result) {
case "trusted":
// The request was signed by a trusted verifier root certificate.
console.log(`Trusted verifier: ${auth.verifierInfo.commonName}`);
break;
case "untrusted":
// A reader signature was present but could not be trusted.
console.warn(`Untrusted verifier: ${auth.error.message}`);
break;
case "unsigned":
// No reader authentication was provided.
console.log(`Unsigned request from: ${auth.origin ?? "unknown origin"}`);
break;
}
});
},
});
if (result.isErr()) {
console.error("Failed to create session:", result.error);
}
```
See [`VerifierInfo`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/types/VerifierInfo.html) and [`VerifierAuthenticationError`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/types/VerifierAuthenticationError.html) for the associated values.
## What the outcomes mean [#what-the-outcomes-mean]
The SDK reports the verifier authentication result but does not act on it. What each outcome tells your application:
* **Trusted**: The request was signed and the signature chained to a trusted verifier root certificate. The `VerifierInfo` identifies which root was matched, including its common name.
* **Untrusted**: A reader signature was present but could not be validated. The result includes the reason and the certificate chain that was presented.
* **Unsigned**: The request carried no reader authentication, so the verifier's identity was not established by the request.
How an application responds to each outcome is a product and trust-framework decision that the SDK does not make for you. Different implementations make different choices depending on their ecosystem and risk tolerance. For example, an application might:
* refuse to respond to unsigned or untrusted requests;
* accept any request and record the outcome for audit;
* present the outcome to the user and let them decide whether to continue;
* proceed automatically for trusted requests while requiring explicit user confirmation for unsigned or untrusted ones.
Whichever approach you take, the outcome is available before you send a response, so you can carry it into your flow and respond only for the credentials involved, as shown in the [Proximity Presentation tutorial](/docs/holding/proximity-presentation-tutorial#step-3-send-a-response).
Many legitimate verifiers do not sign their requests, and validation can fail for benign reasons such as a recently rotated certificate that is not yet trusted on the device. The result describes what could be established about the verifier; what that means for a given interaction depends on your ecosystem.
## Testing verifier authentication [#testing-verifier-authentication]
To exercise each outcome:
1. Claim a credential in your holder application.
2. Present to a verifier that signs its request with a reader certificate that chains to a root trusted on the device, and confirm the result is reported as trusted with the expected common name.
3. Present to a verifier whose reader certificate is not trusted on the device (for example, expired or signed by an unknown root), and confirm the result is reported as untrusted with the expected reason.
4. Present to a verifier that does not sign its request, and confirm the result is reported as unsigned.
# URI scheme handling for verification requests
URL: /docs/holding/remote-presentation-guides/handling-uri-schemes
Description: Learn how to configure your holder application to handle different types of authorization request URI schemes for credential presentations
## Overview [#overview]
When verifiers create OID4VP verification requests and share them with holders, they can use different URI schemes to control which wallet applications can handle those requests and how the user experience flows. Your holder application needs to be properly configured to handle the URI schemes that verifiers in your ecosystem are using.
This guide explains the different URI scheme types and shows you how to configure your holder application to handle each type of URI scheme.
## Understanding URI schemes in verification requests [#understanding-uri-schemes-in-verification-requests]
A URI scheme is the protocol part at the beginning of a URI (such as `https://`, `mailto:`, or custom schemes like `mdoc-openid4vp://`). When a verifier generates an OID4VP verification request, they choose which URI scheme to use based on their requirements for security, user experience, and control over which apps can respond to the request.
Several URI scheme types can be used for OID4VP verification requests:
1. **ISO 18013-7 default scheme** (`mdoc-openid4vp://`): Defined by ISO/IEC 18013-7:2025. A wallet that handles this scheme is declaring compliance with the full ISO 18013-7 configuration — it signals protocol support, not just routing. This includes, among other requirements, support for ECDH-ES as `authorization_encryption_alg_values_supported`. Any app registered to handle this scheme can respond to the authorization request.
2. **Private-use URI scheme** (`com.example.wallet://`): A unique custom scheme using reverse-domain notation. This makes it less likely (but not impossible) for other apps to handle the request. Unlike the default schemes above, a private-use scheme carries no implied protocol configuration — the verifier must either know the wallet's supported capabilities out-of-band, or discover them dynamically (for example, via [OAuth authorization server metadata](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-wallet-metadata-authorizati)).
3. **Claimed HTTPS scheme** (`https://example.com/wallet/...`): Uses domain-verified App Links (Android) or Universal Links (iOS) to ensure only your specific app can handle verification requests from your domain. This is the most secure option, but also requires the most setup and coordination with the verifier to ensure the necessary files and settings are correctly implemented.
For a detailed explanation of how these schemes work, their trade-offs, and security considerations, see the [OID4VP workflow documentation](/docs/verification/remote-web-verifiers/workflow#the-mattr-vii-verifier-tenant-responds-with-a-link-to-invoke-a-matching-application).
## Prerequisites [#prerequisites]
This guide assumes you have:
* Completed the [Remote Presentation Tutorial](/docs/holding/remote-presentation-tutorial) and have a working holder application that can present a credential remotely via OID4VP. All examples in this guide build on top of the tutorial application.
* Understanding of how [OID4VP verification requests](/docs/verification/oid4vp) are created and structured.
* Access to modify your application configuration and code.
For HTTPS schemes (App Links/Universal Links), you will also need:
* A domain you control.
* Ability to host verification files on your web server.
## Configuring URI scheme handling [#configuring-uri-scheme-handling]
The configuration required depends on which URI scheme types you want to support and which platform you're developing for.
### ISO 18013-7 default scheme (`mdoc-openid4vp://`) [#iso-18013-7-default-scheme-mdoc-openid4vp]
The `mdoc-openid4vp://` scheme is defined by ISO/IEC 18013-7:2025 and signals that your wallet supports the static wallet metadata parameters defined in ISO 18013-7. Register this scheme if your holder application is targeting ISO 18013-7 compliance.
**Configure scheme**
1. Open your project in Xcode.
2. Select your app target and navigate to the **Info** tab.
3. Expand **URL Types** and add a new entry with:
* **Identifier**: `mdoc-openid4vp`
* **URL Schemes**: `mdoc-openid4vp`
**Configure intent filter**
1. Open your `AndroidManifest.xml` file and add the following intent filter within your main activity:
```xml title="AndroidManifest.xml"
```
**Configure scheme**
1. Open your `app.config.ts` file and configure the scheme:
```ts title="app.config.ts"
export default ({ config }: ConfigContext): ExpoConfig => ({
// ... other config
scheme: "mdoc-openid4vp",
// ... other config
});
```
For Expo projects, this configuration automatically sets up the scheme for both iOS and Android.
### Private-use URI scheme [#private-use-uri-scheme]
To handle authorization requests using a custom scheme like `com.yourcompany.wallet://`, you need to register that unique scheme with the operating system.
**Step 1: Register your custom scheme**
1. Open your project in Xcode.
2. Select your app target and navigate to the **Info** tab.
3. Expand **URL Types** and select the **+** button.
4. Enter:
* **Identifier**: `com.yourcompany.wallet`
* **URL Schemes**: `com.yourcompany.wallet`
**Step 2: Handle incoming URLs**
In your `ContentView.swift` file, add a handler for the custom scheme URLs. Add the following extension to your ViewModel:
```swift title="ContentView.swift"
// MARK: Handle URL
extension ViewModel {
func handleIncomingURL(_ url: URL) {
guard url.scheme == "com.yourcompany.wallet" else { return }
// Extract the authorization request from the URL
// The request is typically base64-encoded in the path or as a query parameter
if let requestString = extractAuthorizationRequest(from: url) {
Task {
await createOnlinePresentationSession(authorizationRequestURI: requestString)
}
navigationPath.append(NavigationState.onlinePresentation)
}
}
func extractAuthorizationRequest(from url: URL) -> String? {
// Example: com.yourcompany.wallet://authorize/BASE64_ENCODED_REQUEST
if url.host == "authorize",
let encodedRequest = url.pathComponents.last,
let data = Data(base64URLEncoded: encodedRequest),
let decodedRequest = String(data: data, encoding: .utf8) {
return decodedRequest
}
// Alternative: com.yourcompany.wallet://authorize?request=BASE64_ENCODED_REQUEST
if let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let requestParam = components.queryItems?.first(where: { $0.name == "request" })?.value,
let data = Data(base64URLEncoded: requestParam),
let decodedRequest = String(data: data, encoding: .utf8) {
return decodedRequest
}
return nil
}
}
// Helper extension for base64 URL decoding
extension Data {
init?(base64URLEncoded string: String) {
var base64 = string
.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
let paddingLength = (4 - base64.count % 4) % 4
base64.append(String(repeating: "=", count: paddingLength))
self.init(base64Encoded: base64)
}
}
```
Then, add an `onOpenURL` modifier in `ContentView` to handle incoming custom scheme URLs:
```swift title="ContentView.swift"
.onOpenURL { url in
viewModel.handleIncomingURL(url)
}
```
If your app already handles other types of links, you'll need to update the `handleIncomingURL` method to support multiple link types.
**Step 1: Add intent filter for custom scheme**
Open your `AndroidManifest.xml` file and add a new intent filter for your custom scheme:
```xml title="AndroidManifest.xml"
```
**Step 2: Handle incoming intents**
In your `MainActivity.kt` file, add handling for the custom scheme URLs:
```kotlin title="MainActivity.kt"
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Handle intent when activity is created
handleIntent(intent)
setContent {
// ... existing UI code
}
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
// Handle intent when activity is already running
handleIntent(intent)
}
private fun handleIntent(intent: Intent?) {
val data = intent?.data ?: return
when (data.scheme) {
"mdoc-openid4vp" -> {
// Standard scheme handling (already implemented in tutorial)
val authRequest = data.toString()
navigateToOnlinePresentationScreen(authRequest)
}
"com.yourcompany.wallet" -> {
// Custom scheme handling
val authRequest = extractAuthorizationRequest(data)
if (authRequest != null) {
navigateToOnlinePresentationScreen(authRequest)
}
}
}
}
private fun extractAuthorizationRequest(uri: Uri): String? {
// Example: com.yourcompany.wallet://authorize/BASE64_ENCODED_REQUEST
val encodedRequest = uri.lastPathSegment ?: uri.getQueryParameter("request") ?: return null
return try {
val decodedBytes = android.util.Base64.decode(
encodedRequest.replace('-', '+').replace('_', '/'),
android.util.Base64.URL_SAFE or android.util.Base64.NO_PADDING
)
String(decodedBytes, Charsets.UTF_8)
} catch (e: IllegalArgumentException) {
null
}
}
private fun navigateToOnlinePresentationScreen(requestUri: String) {
// Navigate to your online presentation screen with the request URI
// Using the same navigation pattern from the tutorial:
// navController.navigate("onlinePresentation")
// Pass requestUri as needed by your navigation implementation
}
}
```
**Step 1: Update scheme configuration**
Open your `app.config.ts` file and update the scheme to your custom scheme:
```ts title="app.config.ts"
export default ({ config }: ConfigContext): ExpoConfig => ({
// ... other config
scheme: "com.yourcompany.wallet",
// ... other config
});
```
**Step 2: Handle incoming URLs**
In your `/app/_layout.tsx` file, add URL handling using Expo's Linking API:
```tsx title="/app/_layout.tsx"
import { useEffect } from "react";
import * as Linking from "expo-linking";
import { useRouter } from "expo-router";
export default function RootLayout() {
const router = useRouter();
useEffect(() => {
// Handle URL when app is already open
const subscription = Linking.addEventListener("url", ({ url }) => {
handleIncomingURL(url);
});
// Handle URL when app is opened from a closed state
Linking.getInitialURL().then((url) => {
if (url) {
handleIncomingURL(url);
}
});
return () => {
subscription.remove();
};
}, []);
const handleIncomingURL = (url: string) => {
const parsed = Linking.parse(url);
// Handle custom scheme URLs
if (parsed.scheme === "com.yourcompany.wallet" && parsed.hostname === "authorize") {
const authRequest = extractAuthorizationRequest(url);
if (authRequest) {
router.replace({
pathname: "/online-presentation",
params: { scannedValue: authRequest },
});
}
}
};
const extractAuthorizationRequest = (url: string): string | null => {
try {
const parsed = Linking.parse(url);
// Extract from path: com.yourcompany.wallet://authorize/BASE64_ENCODED_REQUEST
const encodedRequest =
(parsed.path ? parsed.path.split("/").filter(Boolean).pop() : undefined) ??
parsed.queryParams?.request;
if (!encodedRequest) return null;
// Decode base64 URL-encoded request
const base64 = encodedRequest
.replace(/-/g, "+")
.replace(/_/g, "/")
.padEnd(encodedRequest.length + ((4 - (encodedRequest.length % 4)) % 4), "=");
return atob(base64);
} catch (error) {
console.error("Failed to extract authorization request:", error);
return null;
}
};
return (
);
}
```
**Step 3: Rebuild native projects**
After changing the scheme configuration, regenerate the native projects:
```bash title="Regenerate native projects"
yarn expo prebuild --clean
```
### Claimed HTTPS scheme (App Links / Universal Links) [#claimed-https-scheme-app-links--universal-links]
HTTPS schemes provide the most secure and reliable way to ensure only your specific app handles authorization requests from your domain. This requires coordination with the verifier to ensure the necessary files and settings are correctly implemented.
**Step 1: Create the Apple App Site Association file**
This step must be performed by the Verifier or the party controlling the domain used in the HTTPS scheme, as it requires hosting a specific file on the web server. If you control both the wallet and the verification service, you can complete this step yourself. Otherwise, you will need to coordinate with the verifier to ensure this file is created and hosted correctly.
Create a file named `apple-app-site-association` (no file extension) with the following content:
```json title="apple-app-site-association"
{
"applinks": {
"apps": [],
"details": [
{
"appID": "TEAM_ID.com.yourcompany.wallet",
"paths": ["/wallet/*"]
}
]
}
}
```
Replace:
* `TEAM_ID` with your Apple Developer Team ID (found in your Apple Developer account).
* `com.yourcompany.wallet` with your app's bundle identifier.
* `/wallet/*` with the path pattern you want to handle (you can use multiple paths).
**Step 2: Host the association file**
Upload the file to your web server at:
```
https://yourdomain.com/.well-known/apple-app-site-association
```
Or directly at the root:
```
https://yourdomain.com/apple-app-site-association
```
Ensure:
* The file is served with `Content-Type: application/json`.
* The file is accessible via HTTPS without redirects.
* No `.json` extension is added to the filename.
**Step 3: Enable Associated Domains capability**
1. Open your project in Xcode.
2. Select your app target and navigate to **Signing & Capabilities**.
3. Select **+ Capability** and add **Associated Domains**.
4. Add your domain in the format: `applinks:yourdomain.com`
**Step 4: Handle Universal Links**
In your `ContentView.swift` file, add a handler for Universal Links. Add the following extension to your ViewModel:
```swift title="ContentView.swift"
// MARK: Handle Universal Links
extension ViewModel {
func handleIncomingURL(_ url: URL) {
guard url.scheme == "https" && url.host == "yourdomain.com" else { return }
// Extract the authorization request from the URL
// Example: https://yourdomain.com/wallet/authorize?request=BASE64_ENCODED_REQUEST
if let requestString = extractAuthorizationRequest(from: url) {
Task {
await createOnlinePresentationSession(authorizationRequestURI: requestString)
}
navigationPath.append(NavigationState.onlinePresentation)
}
}
func extractAuthorizationRequest(from url: URL) -> String? {
// Verify path contains required components
guard url.pathComponents.contains("wallet"),
url.pathComponents.contains("authorize") else { return nil }
// Extract request from query parameter
if let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let requestParam = components.queryItems?.first(where: { $0.name == "request" })?.value,
let data = Data(base64URLEncoded: requestParam),
let decodedRequest = String(data: data, encoding: .utf8) {
return decodedRequest
}
return nil
}
}
// Helper extension for base64 URL decoding
extension Data {
init?(base64URLEncoded string: String) {
var base64 = string
.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
let paddingLength = (4 - base64.count % 4) % 4
base64.append(String(repeating: "=", count: paddingLength))
self.init(base64Encoded: base64)
}
}
```
Then, add an `onOpenURL` modifier in `ContentView` to handle incoming universal links:
```swift title="ContentView.swift"
.onOpenURL { url in
viewModel.handleIncomingURL(url)
}
```
If your app already handles other types of links, you'll need to update the `handleIncomingURL` method to support multiple link types.
**Step 1: Create the Digital Asset Links file**
This step must be performed by the Verifier or the party controlling the domain used in the HTTPS scheme, as it requires hosting a specific file on the web server. If you control both the wallet and the verification service, you can complete this step yourself. Otherwise, you will need to coordinate with the verifier to ensure this file is created and hosted correctly.
Create a file named `assetlinks.json` with the following content:
```json title="assetlinks.json"
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.yourcompany.wallet",
"sha256_cert_fingerprints": [
"YOUR_APP_SHA256_FINGERPRINT"
]
}
}
]
```
Replace:
* `com.yourcompany.wallet` with your app's package name.
* `YOUR_APP_SHA256_FINGERPRINT` with your app's SHA-256 certificate fingerprint.
To get your SHA-256 fingerprint, run:
```bash title="Get SHA-256 fingerprint"
keytool -list -v -keystore your-release-key.keystore
```
Or for debug builds:
```bash title="Get debug SHA-256 fingerprint"
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
```
**Step 2: Host the association file**
Upload the file to your web server at:
```
https://yourdomain.com/.well-known/assetlinks.json
```
Ensure:
* The file is served with `Content-Type: application/json`.
* The file is accessible via HTTPS without redirects.
**Step 3: Add App Links intent filter**
Open your `AndroidManifest.xml` and add an intent filter with `android:autoVerify="true"`:
```xml title="AndroidManifest.xml"
```
**Step 4: Handle App Links**
Update your `MainActivity.kt` to handle App Links:
```kotlin title="MainActivity.kt"
class MainActivity : ComponentActivity() {
private fun handleIntent(intent: Intent?) {
val data = intent?.data ?: return
when {
// Handle App Links (HTTPS URLs)
data.scheme == "https" && data.host == "yourdomain.com" -> {
handleAppLink(data)
}
// Handle custom schemes
data.scheme == "mdoc-openid4vp" || data.scheme == "com.yourcompany.wallet" -> {
handleCustomScheme(data)
}
}
}
private fun handleAppLink(uri: Uri) {
// Example: https://yourdomain.com/wallet/authorize?request=BASE64_ENCODED_REQUEST
if (uri.pathSegments.contains("wallet") && uri.pathSegments.contains("authorize")) {
val encodedRequest = uri.getQueryParameter("request") ?: return
val authRequest = extractAuthorizationRequest(encodedRequest)
if (authRequest != null) {
navigateToAuthScreen(authRequest)
}
}
}
private fun handleCustomScheme(uri: Uri) {
// ... existing custom scheme handling
}
private fun extractAuthorizationRequest(encodedRequest: String): String? {
return try {
val decodedBytes = android.util.Base64.decode(
encodedRequest.replace('-', '+').replace('_', '/'),
android.util.Base64.URL_SAFE or android.util.Base64.NO_PADDING
)
String(decodedBytes, Charsets.UTF_8)
} catch (e: IllegalArgumentException) {
null
}
}
```
**Step 5: Verify App Links**
After installing your app, verify that App Links are working:
```bash title="Verify App Links"
adb shell am start -a android.intent.action.VIEW -d "https://yourdomain.com/wallet/authorize?request=test"
```
You can also check the verification status:
```bash title="Check verification status"
adb shell pm get-app-links com.yourcompany.wallet
```
For React Native with Expo, configuring Universal Links (iOS) and App Links (Android) requires additional setup in the app configuration.
**Step 1: Create association files**
This step must be performed by the Verifier or the party controlling the domain used in the HTTPS scheme, as it requires hosting a specific file on the web server. If you control both the wallet and the verification service, you can complete this step yourself. Otherwise, you will need to coordinate with the verifier to ensure this file is created and hosted correctly.
Follow the instructions in the iOS and Android tabs to create and host:
* `apple-app-site-association` file at `https://yourdomain.com/.well-known/apple-app-site-association`
* `assetlinks.json` file at `https://yourdomain.com/.well-known/assetlinks.json`
**Step 2: Configure app for Universal Links / App Links**
Update your `app.config.ts`:
```ts title="app.config.ts"
export default ({ config }: ConfigContext): ExpoConfig => ({
// ... other config
ios: {
bundleIdentifier: "com.yourcompany.wallet",
associatedDomains: ["applinks:yourdomain.com"],
// ... other iOS config
},
android: {
package: "com.yourcompany.wallet",
intentFilters: [
{
action: "VIEW",
autoVerify: true,
data: [
{
scheme: "https",
host: "yourdomain.com",
pathPrefix: "/wallet",
},
],
category: ["BROWSABLE", "DEFAULT"],
},
],
// ... other Android config
},
// ... other config
});
```
**Step 3: Handle Universal Links / App Links**
Update your `/app/_layout.tsx` to handle HTTPS URLs:
```tsx title="/app/_layout.tsx"
import { useEffect } from "react";
import * as Linking from "expo-linking";
import { useRouter } from "expo-router";
export default function RootLayout() {
const router = useRouter();
useEffect(() => {
const subscription = Linking.addEventListener("url", ({ url }) => {
handleIncomingURL(url);
});
Linking.getInitialURL().then((url) => {
if (url) {
handleIncomingURL(url);
}
});
return () => {
subscription.remove();
};
}, []);
const handleIncomingURL = (url: string) => {
const parsed = Linking.parse(url);
// Handle Universal Links / App Links (HTTPS)
if (parsed.scheme === "https" && parsed.hostname === "yourdomain.com") {
handleAppLink(url);
return;
}
// Handle custom schemes
if (
parsed.scheme === "mdoc-openid4vp" ||
parsed.scheme === "com.yourcompany.wallet"
) {
handleCustomScheme(url);
return;
}
};
const handleAppLink = (url: string) => {
const parsed = Linking.parse(url);
// Example: https://yourdomain.com/wallet/authorize?request=BASE64_ENCODED_REQUEST
if (parsed.path?.includes("/wallet/authorize")) {
const encodedRequest = parsed.queryParams?.request as string;
if (encodedRequest) {
const authRequest = extractAuthorizationRequest(encodedRequest);
if (authRequest) {
router.replace({
pathname: "/online-presentation",
params: { scannedValue: authRequest },
});
}
}
}
};
const handleCustomScheme = (url: string) => {
// ... existing custom scheme handling
};
const extractAuthorizationRequest = (encodedRequest: string): string | null => {
try {
const base64 = encodedRequest
.replace(/-/g, "+")
.replace(/_/g, "/")
.padEnd(encodedRequest.length + ((4 - (encodedRequest.length % 4)) % 4), "=");
return atob(base64);
} catch (error) {
console.error("Failed to decode authorization request:", error);
return null;
}
};
return (
);
}
```
**Step 4: Rebuild native projects**
After updating the configuration, regenerate the native projects:
```bash title="Regenerate native projects"
yarn expo prebuild --clean
```
**Step 5: Build and test**
For iOS, you must test Universal Links on a physical device (not simulator) with a production or ad-hoc build.
For Android, install the app and verify App Links as described in the Android tab above.
## Testing your URI scheme implementation [#testing-your-uri-scheme-implementation]
After configuring your app to handle different URI schemes, test each implementation to ensure it works correctly.
### Testing with QR codes [#testing-with-qr-codes]
Create an authorization request and generate QR codes for each URI scheme type you support:
1. **ISO 18013-7 scheme**: `mdoc-openid4vp://authorize?request=ey...`
2. **Custom scheme**: `com.yourcompany.wallet://authorize?request=ey...`
3. **HTTPS scheme**: `https://yourdomain.com/wallet/authorize?request=ey...`
You can use online QR code generators or create them programmatically for testing.
### Testing with deep links [#testing-with-deep-links]
A deep link works the same way as a QR code — pass the full authorization request URI directly. For iOS, use the following command to test on a simulator:
```bash title="Test iOS deep link"
xcrun simctl openurl booted "com.yourcompany.wallet://authorize?request=ey..."
```
For Android, use:
```bash title="Test Android deep link"
adb shell am start -a android.intent.action.VIEW -d "com.yourcompany.wallet://authorize?request=ey..."
```
### Testing Universal Links / App Links [#testing-universal-links--app-links]
For iOS Universal Links, you must test on a physical device with a production or ad-hoc build. Links opened in Safari should open your app.
For Android App Links, use:
```bash title="Test Android App Link"
adb shell am start -a android.intent.action.VIEW -d "https://yourdomain.com/wallet/authorize?request=ey..."
```
## Common issues and troubleshooting [#common-issues-and-troubleshooting]
### App doesn't open when scanning QR code or tapping link [#app-doesnt-open-when-scanning-qr-code-or-tapping-link]
**Possible causes:**
* URI scheme not properly registered in the app configuration.
* For Universal Links/App Links, association files not properly hosted or accessible.
* For Universal Links/App Links, domain not added to associated domains / intent filters.
**Solutions:**
* Verify URL scheme registration in your app configuration.
* Test the association file URLs directly in a browser to ensure they're accessible.
* Check that the association file content matches your app's bundle ID/package name and certificate.
* For iOS, verify Associated Domains capability is enabled and domains are correctly listed.
* For Android, verify `android:autoVerify="true"` is set and check verification status with `adb shell pm get-app-links`.
### Wrong app opens when multiple wallet apps are installed [#wrong-app-opens-when-multiple-wallet-apps-are-installed]
**Possible causes:**
* Multiple apps registered for the same URI scheme (common with `mdoc-openid4vp://`).
* Association files not properly verified (for Universal Links/App Links).
**Solutions:**
* Use a unique custom scheme or domain-verified HTTPS scheme.
* For Universal Links/App Links, ensure association files are correctly configured and verified.
* Test on a clean device or uninstall competing apps during testing.
### Encoded request fails to decode [#encoded-request-fails-to-decode]
**Possible causes:**
* Incorrect base64 URL encoding/decoding.
* Request parameter not properly extracted from URL.
**Solutions:**
* Ensure you're using base64 URL encoding (not standard base64) with `-` and `_` instead of `+` and `/`.
* Verify padding is handled correctly when decoding.
* Log the encoded and decoded values to debug the transformation.
## Related resources [#related-resources]
* [iOS Universal Links documentation](https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app)
* [Android App Links documentation](https://developer.android.com/training/app-links)
* [OpenID for Verifiable Presentations (OID4VP)](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html)
* [OAuth 2.0 for Native Apps (RFC 8252)](https://datatracker.ietf.org/doc/html/rfc8252)
# Handling verifier authentication
URL: /docs/holding/remote-presentation-guides/verifier-authentication
Description: Establish and inspect how a remote verifier was authenticated so users can decide whether to share credentials with an unauthenticated verifier
## Overview [#overview]
When a verifier requests credentials remotely over OpenID for Verifiable Presentations (OID4VP), your holder application can establish whether the verifier proved its identity before any credential data is shared. This lets you show the user who is asking for their data and how that identity was established, and lets you refuse to proceed with a verifier that cannot be trusted.
In a remote presentation, the verifier signs the authorization request. The Holder SDK validates that signature when it creates the presentation session, and reports how the verifier was authenticated. Your application controls how strict that validation must be, and reads the outcome to inform the user.
This guide covers remote (OID4VP / ISO/IEC 18013-7) verifier authentication, where the verifier signs the authorization request and trust is established when the session is created. Proximity (ISO/IEC 18013-5) presentations authenticate the verifier through a signed device request instead, which ISO/IEC 18013-5 calls *mDoc Reader Authentication*. The `verifierAuthenticationResult` described in the [proximity guide](/docs/holding/proximity-presentation-guides/verifier-authentication) does not apply to remote presentations; the remote equivalent is the `requireTrustedVerifier` option and the `verifiedBy` result described below.
## Prerequisites [#prerequisites]
This guide builds on the [Remote Presentation tutorial](/docs/holding/remote-presentation-tutorial). Complete that tutorial first so you have a working holder application that can create an online presentation session from an authorization request, then return here to add verifier authentication handling.
## Understanding remote verifier authentication [#understanding-remote-verifier-authentication]
Under ISO/IEC 18013-7:2025, the authorization request is delivered as a signed request object (a JWT) using the `x509_san_dns` client identifier scheme. The verifier's `client_id` must be a DNS name that matches a `dNSName` Subject Alternative Name in the certificate that signed the request. When you create the session, the SDK validates the request signature and then reports how the verifier was authenticated through a `verifiedBy` result with one of two outcomes:
* **Certificate**: The request signature was validated against a trusted verifier root certificate configured on the device. This is the stronger outcome, because the verifier's certificate is explicitly trusted ahead of time. The result carries the certificate's common name.
* **Domain**: The request signature was validated using the verifier's published client metadata, bound to its DNS name through the `x509_san_dns` scheme, rather than against a pre-configured trusted certificate. The result carries the `client_id` from the request.
You control which of these is acceptable with the `requireTrustedVerifier` option when you create the session:
* When `requireTrustedVerifier` is `false` (the default), the SDK first attempts validation against the stored trusted verifier certificates. If that fails, it falls back to resolving the verifier's client metadata and validating against the keys published there. A successful session is reported as either `Certificate` or `Domain` accordingly.
* When `requireTrustedVerifier` is `true`, the SDK only accepts a request that validates against a stored trusted verifier certificate. If no trusted certificate matches, session creation fails rather than falling back to domain validation.
If validation fails, session creation reports an error: validation against a trusted certificate failing surfaces as an "invalid authorization request, verified by certificate" error, and domain validation failing surfaces as an "invalid authorization request, verified by domain" error.
## Adding trusted verifier certificates [#adding-trusted-verifier-certificates]
A `Certificate` result, and the `requireTrustedVerifier` option, both depend on trusted verifier root certificates stored on the device. Your application provisions these certificates through the SDK. The same trusted verifier certificate store is used for both remote and [proximity](/docs/holding/proximity-presentation-guides/verifier-authentication) presentations.
Certificates are supplied as PEM-encoded or Base64-encoded DER strings. Adding a certificate is idempotent: adding the same certificate again returns the identifier of the existing entry rather than creating a duplicate.
Use [`addTrustedVerifierCertificates`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/addtrustedverifiercertificates\(certificates:\)), [`getTrustedVerifierCertificates`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/gettrustedverifiercertificates\(\)), and [`deleteTrustedVerifierCertificate`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/deletetrustedverifiercertificate\(certificateid:\)):
```swift title="CertificateSettings.swift"
let mobileCredentialHolder = MobileCredentialHolder.shared
// Add one or more trusted verifier certificates (PEM or Base64 DER).
let addedIds = try mobileCredentialHolder.addTrustedVerifierCertificates(
certificates: [verifierCertificatePem]
)
// List the stored trusted verifier certificates.
let stored: [TrustedCertificate] = try mobileCredentialHolder.getTrustedVerifierCertificates()
// Remove one by its identifier.
try mobileCredentialHolder.deleteTrustedVerifierCertificate(certificateId: addedIds[0])
```
Each stored certificate is a [`TrustedCertificate`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/trustedcertificate) carrying its `id`, `pem`, and `commonName`.
Use [`addTrustedVerifierCertificates`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/add-trusted-verifier-certificates.html), [`getTrustedVerifierCertificates`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/get-trusted-verifier-certificates.html), and [`deleteTrustedVerifierCertificate`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/delete-trusted-verifier-certificate.html):
```kotlin title="CertificateSettings.kt"
val mobileCredentialHolder = MobileCredentialHolder.getInstance()
// Add one or more trusted verifier certificates (PEM or Base64 DER).
val addedIds: List = mobileCredentialHolder.addTrustedVerifierCertificates(
listOf(verifierCertificatePem)
)
// List the stored trusted verifier certificates.
val stored: List = mobileCredentialHolder.getTrustedVerifierCertificates()
// Remove one by its identifier.
mobileCredentialHolder.deleteTrustedVerifierCertificate(addedIds.first())
```
Each stored certificate is a `TrustedCertificate` carrying its `id`, `pem`, and `commonName`.
Use [`addTrustedVerifierCertificates`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/addTrustedVerifierCertificates.html), [`getTrustedVerifierCertificates`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/getTrustedVerifierCertificates.html), and [`deleteTrustedVerifierCertificate`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/deleteTrustedVerifierCertificate.html):
```ts title="trustedVerifierCertificates.ts"
import {
addTrustedVerifierCertificates,
getTrustedVerifierCertificates,
deleteTrustedVerifierCertificate,
} from "@mattrglobal/mobile-credential-holder-react-native";
// Add one or more trusted verifier certificates (PEM or Base64 DER).
const result = await addTrustedVerifierCertificates([verifierCertificatePem]);
if (result.isErr()) {
console.error("Failed to add trusted verifier certificates:", result.error);
return;
}
const addedIds = result.value;
// List the stored trusted verifier certificates.
const stored = await getTrustedVerifierCertificates();
// Remove one by its identifier.
await deleteTrustedVerifierCertificate(addedIds[0]);
```
Each stored certificate is a [`TrustedVerifierCertificate`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/types/TrustedVerifierCertificate.html) carrying its `id`, `pem`, and `commonName`.
## Creating the session and inspecting the result [#creating-the-session-and-inspecting-the-result]
Create the session as in the [Remote Presentation tutorial](/docs/holding/remote-presentation-tutorial#step-2-create-an-online-presentation-session), choosing the value of `requireTrustedVerifier` that fits your trust policy, then read `verifiedBy` from the returned session before presenting the consent UI.
Pass `requireTrustedVerifier` to [`createOnlinePresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/createonlinepresentationsession\(authorisationrequesturi:requiretrustedverifier:\)) and switch over the `verifiedBy` property of the returned [`OnlinePresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/onlinepresentationsession):
```swift title="ViewModel.swift"
func createOnlinePresentationSession(authorizationRequestUri: String) async {
do {
let session = try await mobileCredentialHolder.createOnlinePresentationSession(
authorizationRequestUri: authorizationRequestUri,
requireTrustedVerifier: false
)
switch session.verifiedBy {
case .certificate(let commonName):
// Validated against a trusted verifier root certificate.
print("Verified by certificate: \(commonName)")
case .domain(let clientId):
// Validated against the verifier's published client metadata.
print("Verified by domain: \(clientId)")
}
// Continue with session.matchedCredentials and your consent UI.
} catch MobileCredentialHolderError.invalidAuthorizationRequestVerifiedByCertificate {
// requireTrustedVerifier was true and no trusted certificate matched.
} catch MobileCredentialHolderError.invalidAuthorizationRequestVerifiedByDomain {
// The request could not be validated against the verifier's domain.
} catch {
// Handle other errors
}
}
```
Pass `requireTrustedVerifier` to [`createOnlinePresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/create-online-presentation-session.html) and match over the `verifiedBy` property of the returned [`OnlinePresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.onlinepresentation/-online-presentation-session/index.html):
```kotlin title="OnlinePresentationViewModel.kt"
try {
val session = MobileCredentialHolder.getInstance().createOnlinePresentationSession(
authorizationRequestUri = requestUri,
requireTrustedVerifier = false
)
when (val verifiedBy = session.verifiedBy) {
is VerifiedBy.Certificate ->
// Validated against a trusted verifier root certificate.
Log.i(TAG, "Verified by certificate: ${verifiedBy.commonName}")
is VerifiedBy.Domain ->
// Validated against the verifier's published client metadata.
Log.i(TAG, "Verified by domain: ${verifiedBy.domain}")
}
// Continue with session.getMatchedCredentials() and your consent UI.
} catch (e: HolderException.InvalidAuthorizationRequestVerifiedByCertificateException) {
// requireTrustedVerifier was true and no trusted certificate matched.
} catch (e: Throwable) {
// Handle other errors
}
```
See [`VerifiedBy`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.onlinepresentation/-verified-by/index.html) for the associated values.
Pass `requireTrustedVerifier` to [`createOnlinePresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/createOnlinePresentationSession.html) and read the `verifiedBy` property of the returned [`OnlinePresentationSession`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/types/OnlinePresentationSession.html). The function returns a `Result`, so the untrusted case is an error value rather than a thrown exception:
```ts title="usePresentations.ts"
const result = await createOnlinePresentationSession({
authorizationRequestUri,
requireTrustedVerifier: false,
});
if (result.isErr()) {
const { error } = result;
if (error.type === MobileCredentialHolderErrorType.InvalidAuthorizationRequestVerifiedByCertificate) {
// requireTrustedVerifier was true and no trusted certificate matched.
}
// Handle other errors
return;
}
const session = result.value;
switch (session.verifiedBy.type) {
case OnlinePresentationSessionVerifiedByType.Certificate:
// Validated against a trusted verifier root certificate.
console.log(`Verified by certificate: ${session.verifiedBy.value}`);
break;
case OnlinePresentationSessionVerifiedByType.Domain:
// Validated against the verifier's published client metadata.
console.log(`Verified by domain: ${session.verifiedBy.value}`);
break;
}
// Continue with session.matchedCredentials and your consent UI.
```
See [`OnlinePresentationSessionVerifiedBy`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/types/OnlinePresentationSessionVerifiedBy.html) for the result shape.
## What the outcomes mean [#what-the-outcomes-mean]
The `verifiedBy` result describes how the verifier's identity was established. What each outcome tells your application:
* **Certificate**: The request signature was validated against a trusted verifier root certificate stored on the device. The result carries the certificate's common name.
* **Domain**: The request signature was validated through the verifier's published client metadata, bound to its DNS name through the `x509_san_dns` scheme, rather than against a pre-configured certificate. The result carries the `client_id` from the request.
* **Validation failed**: Session creation did not succeed, and no session is returned.
How an application uses this is a product and trust-framework decision that the SDK does not make for you. The `requireTrustedVerifier` option and the `verifiedBy` result give you two complementary controls, and different implementations combine them differently depending on their ecosystem and risk tolerance. For example, an application might:
* set `requireTrustedVerifier` to `true` so that only pre-vetted verifiers can create a session, and domain-only verifiers are rejected at session creation;
* leave `requireTrustedVerifier` as `false` and accept both outcomes, recording how trust was established for audit;
* present the `verifiedBy` result to the user and let them decide whether to continue;
* proceed automatically for a `Certificate` result while requiring explicit user confirmation for a `Domain` result.
Whichever approach you take, you have the outcome before presenting any data, so you can carry it into your flow and respond only for the credentials involved, as shown in the [Remote Presentation tutorial](/docs/holding/remote-presentation-tutorial#step-4-send-response).
A `Domain` result means the request was validly signed and bound to the verifier's DNS name, but it does not mean the verifier has been vetted against a trust framework ahead of time. Whether that distinction matters for a given interaction depends on your ecosystem.
## Testing remote verifier authentication [#testing-remote-verifier-authentication]
To exercise each outcome:
1. Claim a credential in your holder application.
2. Present to a verifier whose request is signed by a certificate trusted on the device, and confirm the session is reported as verified by certificate with the expected common name.
3. Present to a verifier whose request validates only through its published client metadata, and confirm the session is reported as verified by domain with the expected `client_id`.
4. Set `requireTrustedVerifier` to `true` and repeat step 3, confirming that session creation now fails with an "invalid authorization request, verified by certificate" error.
# mDocs remote web presentation journey pattern
URL: /docs/holding/remote-presentation-journey-patterns/mobile
This journey pattern assumes that the wallet is presenting a credential to a
verifier using MATTR verification capabilities. However, the same pattern can
be applied to any ISO/IEC 18013-7 compliant verifier.
This journey pattern is used to verify an mDoc remotely by presenting it to an app installed on the same mobile device as the digital wallet, as per
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html).
## Overview [#overview]
* **Issuance channel**: Remote, unsupervised
* **Device/s**: Same-device
* **Formats**: mDocs
* **Information assurance level**: High
* **Identity assurance level**: High
## Journey flow [#journey-flow]
## Architecture [#architecture]
### Interacting with the mobile application [#interacting-with-the-mobile-application]
The user accesses a mobile app that embeds the MATTR Pi Verifier Mobile SDK. The app initiates and handles the entire verification flow on the same device.
### Requesting a credential for verification [#requesting-a-credential-for-verification]
The Verifier Mobile SDK sends a verification request to a configured MATTR VII verifier tenant, defining:
* The credentials and claims required
* The supported interaction mode (same-device)
The MATTR VII verifier tenant is configured with:
* Which apps or domains can issue verification requests
* The workflows it supports (same-device and/or cross-device)
* The protocols it supports (e.g. OID4VP, Apple’s Verify with Wallet API)
* Which wallet applications it can invoke on the same device
The verifier tenant responds with a custom URI or universal link. The Verifier Mobile SDK uses this to launch the wallet app directly.
### Presenting request details to the user [#presenting-request-details-to-the-user]
The wallet retrieves the presentation request and displays:
* The credentials requested
* The claims that will be shared
* Whether the relying party is trusted by the Digital Trust Service
* Which of the user’s credentials match the request
The user authenticates and consents to share the requested information.
### Verifying the credential [#verifying-the-credential]
The MATTR VII verifier tenant verifies the credential by checking:
* Validating the digital signature to confirm the data has not been tampered with
* Checking that the credential has not been revoked or suspended, using a revocation list (if applicable)
* Verifying that the credential is currently valid, based on its “valid from” and “valid until” dates
* Ensuring the credential was issued by a trusted issuer, based on information retrieved from a Digital Trust Service
The issuer of the credential is not informed that the presentation has occurred. No data about the verifier, the context of use, or the interaction itself is shared with the issuer. The only interaction with the issuer is a potential call to an online revocation endpoint, if revocation checking is required.
### Displaying verification results [#displaying-verification-results]
Once verification is complete, the wallet app redirects the user back to the mobile application using the provided redirect URI.
The Verifier Mobile SDK receives the result and displays it within the app interface.
The MATTR VII verifier tenant can also be configured to share the verification
results with a configured back-end rather than the front-end directly.
# mDocs remote web presentation journey pattern
URL: /docs/holding/remote-presentation-journey-patterns/web
This journey pattern assumes that the wallet is presenting a credential to a verifier using MATTR verification capabilities. However, the same pattern can be applied to any ISO/IEC 18013-7 compliant verifier.
This journey pattern is used to verify an mDoc remotely via an
[online verification workflow](/docs/verification/remote-overview), as per
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html).
## Overview [#overview]
* **Issuance channel**: Remote, unsupervised
* **Device/s**: Same-device / Cross-device
* **Formats**: mDocs
* **Information assurance level**: High
* **Identity assurance level**: High
## Journey flow - Same-device [#journey-flow---same-device]
## Architecture - Same-device [#architecture---same-device]
### Interacting with the website [#interacting-with-the-website]
The user accesses a website using a mobile browser on the same device that holds their wallet app.
### Requesting a credential for verification [#requesting-a-credential-for-verification]
On the website, the user begins an interaction that requires them to present a credential.
The MATTR Pi Verifier Web SDK, embedded in the web application, initiates the verification request by sending it to a configured MATTR VII verifier tenant. This request defines:
* The credentials and claims required
* The supported interaction modes (same-device, in this case)
The MATTR VII verifier tenant is configured with:
* The domains it can accept requests from
* The workflows it supports (e.g. same-device and/or cross-device)
* The supported protocols (e.g. Digital Credentials API and/or OID4VP)
* The wallet applications it can interact with
* How to invoke these wallet applications on the same device
Based on this configuration, the MATTR VII verifier tenant identifies that the user is using a mobile browser and returns a universal link or custom URI that can invoke the wallet app.
The Verifier Web SDK uses this link to redirect the user to their wallet application.
### Presenting request details to the user [#presenting-request-details-to-the-user]
Once the wallet is launched, it authenticates the user and retrieves the verification request from the MATTR VII verifier tenant. The user is shown:
* The credentials being requested
* The claims required from those credentials
* Whether the relying party is trusted and authorized by the Digital Trust Service
* Which of the user’s credentials match the request
The user can then review and consent to sharing the information.
### Verifying the credential [#verifying-the-credential]
The MATTR VII verifier tenant verifies the presentation by checking:
* That the credential has not been tampered with
* That it has not been revoked or suspended
* That it has not expired
* That it was issued by a trusted issuer, validated via the configured Digital Trust Service
### Displaying verification results [#displaying-verification-results]
After the verification is complete, the wallet app redirects the user back to their mobile browser, returning them to the original web application using the previously provided redirect URl.
The Verifier Web SDK receives the verification result and renders it in the browser, allowing the user to view the outcome and continue their interaction.
The issuer of the credential is not informed that the presentation has occurred. No data about the verifier, the context of use, or the interaction itself is shared with the issuer. The only interaction with the issuer is a potential call to an online revocation endpoint, if revocation checking is required.
The MATTR VII verifier tenant can also be configured to return the verification result to a secure back-end service instead of the front-end, depending on implementation needs.
## Journey flow - Cross-device [#journey-flow---cross-device]
## Architecture - Cross-device [#architecture---cross-device]
### Interacting with the website [#interacting-with-the-website-1]
The user accesses a website using a web browser on their desktop.
### Requesting a credential for verification [#requesting-a-credential-for-verification-1]
On the website, the user begins an interaction that requires them to present a credential.
The MATTR Pi Verifier Web SDK, embedded in the web application, initiates the verification request by sending it to a configured MATTR VII verifier tenant. This request defines:
* The credentials and claims required
* The supported interaction modes (same-device, in this case)
The MATTR VII verifier tenant is configured with:
* The domains it can accept requests from
* The workflows it supports (e.g. same-device and/or cross-device)
* The supported protocols (e.g. Digital Credentials API and/or OID4VP)
* The wallet applications it can interact with
* How to invoke these wallet applications on the same device
Based on this configuration, the MATTR VII verifier tenant returns a link. The Verifier Web SDK recognizes Samantha is using a desktop browser and renders this link as a QR code on the webpage.
The user scans the QR code using a mobile device that has a compatible digital wallet installed. This action invokes the wallet app with the verification request.
### Presenting request details to the user [#presenting-request-details-to-the-user-1]
Once the wallet is launched, it authenticates the user and retrieves the verification request from the MATTR VII verifier tenant. The user is shown:
* The credentials being requested
* The claims required from those credentials
* Whether the relying party is trusted and authorized by the Digital Trust Service
* Which of the user’s credentials match the request
The user can then review and consent to sharing the information.
### Verifying the credential [#verifying-the-credential-1]
The MATTR VII verifier tenant verifies the presentation by checking:
* That the credential has not been tampered with
* That it has not been revoked or suspended
* That it has not expired
* That it was issued by a trusted issuer, validated via the configured Digital Trust Service
### Displaying verification results [#displaying-verification-results-1]
The MATTR VII verifier tenant shares the verification results with the Verifier Web SDK. These results are displayed to the user in the browser, allowing them to continue their interaction
The issuer of the credential is not informed that the presentation has occurred. No data about the verifier, the context of use, or the interaction itself is shared with the issuer. The only interaction with the issuer is a potential call to an online revocation endpoint, if revocation checking is required.
The MATTR VII verifier tenant can also be configured to return the verification result to a secure back-end service instead of the front-end, depending on implementation needs.
# Configure activity logs
URL: /docs/holding/sdk-operations/activity-log
Description: Learn how to configure activity logging in the MATTR Holder SDKs to record credential and presentation events for display in your wallet application.
The MATTR Holder SDKs include an activity logging system that records user-facing events such as
credential additions, removals, and presentations. Unlike
[SDK logging](/docs/holding/sdk-operations/sdk-logging), which captures internal diagnostic
information for developers, activity logs are designed to power end-user features in your wallet
application, such as a credential usage history.
## How activity logs differ from SDK logs [#how-activity-logs-differ-from-sdk-logs]
The activity log and SDK log serve different purposes and audiences, even though they both capture events related to the SDK's operation. The following table summarizes the key differences:
| Feature | Activity logs | SDK logs |
| ----------------- | ------------------------------------ | -------------------------------------------------- |
| **Purpose** | User-facing credential usage history | Developer diagnostics and debugging |
| **Audience** | End users of your wallet application | Developers integrating the SDK |
| **Content** | Credential and presentation events | Internal SDK operations, errors, and state changes |
| **Configuration** | `activityLogConfiguration` | `loggerConfiguration` |
## Privacy considerations [#privacy-considerations]
Activity logs are stored **only on the user's device**. They are never shared with issuers,
verifiers, or any external party as part of credential issuance or presentation interactions.
Activity logs are intended to give users visibility into how their credentials have been used. This
enables you to build transparency features into your wallet application, such as:
* Showing the user when and where a credential was presented.
* Displaying a history of credential additions and removals.
* Allowing users to review and clear their activity history.
Because this data stays on-device, it supports user sovereignty and trust without creating additional
privacy risks.
## What events are recorded [#what-events-are-recorded]
The activity log records two types of events:
* **Credential events**: Recorded when a credential is added to or removed from storage. Each event
includes the credential identifier, document type, issuer details, and optional branding
information.
* **Presentation events**: Recorded when a credential is presented to a verifier, or when a
presentation request is declined. Each event includes details about what was requested, what was
shared, and the type of session (proximity or online). Where available, the SDK also records the
identity of the requesting verifier based on a trusted verifier certificate or, for online
sessions, the verifier's domain.
Each activity log entry contains a timestamp and one or more events that occurred at that point in
time.
## Enable activity logging at initialization [#enable-activity-logging-at-initialization]
Activity logging is configured by passing an `ActivityLogConfiguration` object to the SDK's
`initialize` method (this is separate from the `loggerConfiguration` used for SDK diagnostic logs).
```swift title="Enable activity logging during initialization"
try await mobileCredentialHolder.initialize(
activityLogConfiguration: ActivityLogConfiguration( // [!code highlight]
isEnabled: true // [!code highlight]
) // [!code highlight]
)
```
* `isEnabled`: Set to `true` to start recording activity log events immediately after
initialization. Defaults to `true`.
Refer to the
[`ActivityLogConfiguration`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/activitylogconfiguration)
reference documentation for additional details.
```kotlin title="Enable activity logging during initialization"
mobileCredentialHolder.initialize(
activityLogConfiguration = ActivityLogConfiguration(
isEnabled = true
),
// ...
)
```
* `isEnabled`: Set to `true` to start recording activity log events immediately after
initialization. Defaults to `true`.
Refer to the
[`ActivityLogConfiguration`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.activitylog.dto/-activity-log-configuration/index.html)
reference documentation for additional details.
Coming soon...
## Toggle activity logging at runtime [#toggle-activity-logging-at-runtime]
You can enable or disable activity logging after initialization without reinitializing the SDK. This
overrides the value set in `activityLogConfiguration` during initialization, and is useful for
allowing users to opt in or out of activity tracking from your application's settings.
```swift title="Toggle activity logging"
// Enable activity logging
try mobileCredentialHolder.setActivityLogEnabled(true)
// Disable activity logging
try mobileCredentialHolder.setActivityLogEnabled(false)
// Check if activity logging is currently enabled
let isEnabled = mobileCredentialHolder.isActivityLogRecordingEnabled
```
* [`setActivityLogEnabled(_:)`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/setactivitylogenabled\(_:\)):
Enable or disable activity log recording.
* [`isActivityLogRecordingEnabled`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/isactivitylogrecordingenabled/):
A Boolean indicating whether activity log recording is currently enabled.
```kotlin title="Toggle activity logging"
// Enable activity logging
mobileCredentialHolder.setActivityLogEnabled(true)
// Disable activity logging
mobileCredentialHolder.setActivityLogEnabled(false)
// Check if activity logging is currently enabled
val isEnabled = mobileCredentialHolder.isActivityLogRecordingEnabled()
```
* [`setActivityLogEnabled`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/set-activity-log-enabled.html):
Enable or disable activity log recording.
* [`isActivityLogRecordingEnabled`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/is-activity-log-recording-enabled.html):
Returns a Boolean indicating whether activity log recording is currently enabled.
Coming soon...
## Retrieve activity log entries [#retrieve-activity-log-entries]
You can retrieve recorded activity log entries. Each entry contains a
timestamp and a list of events that occurred at that point in time. Events are either credential
events (additions or removals) or presentation events (shares or declines).
```swift title="Retrieve activity log entries"
// Get the first 50 entries
let entries = try mobileCredentialHolder.getActivityLog(count: 50, offset: 0)
for entry in entries {
print("Entry \(entry.id) at \(entry.dateTime)")
for event in entry.events {
switch event {
case .credential(let credentialEvent):
print(" Credential event: \(credentialEvent.eventType) - \(credentialEvent.docType)")
case .presentation(let presentationEvent):
print(" Presentation event: \(presentationEvent.eventType)")
}
}
}
```
The
[`getActivityLog(count:offset:)`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/getactivitylog\(count:offset:\))
method returns an array of
[`ActivityLogEntry`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/activitylogentry/)
objects. Each entry contains:
* `id`: A unique identifier for the entry.
* `dateTime`: The timestamp when the entry was recorded.
* `events`: An array of
[`ActivityLogEvent`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/activitylogevent)
values, which are either
[`credential`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/activitylogcredentialevent) (representing credential additions or removals)
or
[`presentation`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/activitylogpresentationevent) (representing presentation events)
events.
Parameters:
* `count`: The maximum number of entries to return.
* `offset`: The number of entries to skip (for pagination).
```kotlin title="Retrieve activity log entries"
// Get the first 50 entries
val entries = mobileCredentialHolder.getActivityLog(count = 50, offset = 0)
entries.forEach { entry ->
Log.d("Test", "Entry ${entry.id} at ${entry.dateTime}")
entry.events.forEach { event ->
when (event) {
is ActivityLogCredentialEvent ->
Log.d("Test", "Credential event: ${event.eventType} - ${event.docType}")
is ActivityLogPresentationEvent ->
Log.d("Test", "Presentation event: ${event.eventType}")
}
}
}
```
The
[`getActivityLog`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/get-activity-log.html)
method returns a list of
[`ActivityLogEntry`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.activitylog.dto/-activity-log-entry/index.html)
objects. Each entry contains:
* `id`: A unique identifier for the entry.
* `dateTime`: The timestamp when the entry was recorded.
* `events`: A list of
[`ActivityLogEvent`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.activitylog.dto/-activity-log-event/index.html)
instances, which are either
[`ActivityLogCredentialEvent`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.activitylog.dto/-activity-log-credential-event/index.html) (representing credential additions or removals)
or
[`ActivityLogPresentationEvent`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.activitylog.dto/-activity-log-presentation-event/index.html) (representing presentation events).
Parameters:
* `count`: The maximum number of entries to return. Defaults to `100`.
* `offset`: The number of entries to skip (for pagination). Defaults to `0`.
Coming soon.
## Clear the activity log [#clear-the-activity-log]
You can clear all activity log entries from the device. This permanently deletes all recorded
events and cannot be undone. You might expose this option to users as a way to reset their activity
history.
```swift title="Clear all activity log entries"
try mobileCredentialHolder.clearActivityLog()
```
The
[`clearActivityLog()`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/clearactivitylog\(\))
method removes all activity log entries from the device.
```kotlin title="Clear all activity log entries"
mobileCredentialHolder.clearActivityLog()
```
The
[`clearActivityLog`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/clear-activity-log.html)
method removes all activity log entries from the device.
Coming soon...
# Configure SDK logging
URL: /docs/holding/sdk-operations/sdk-logging
Description: Learn how to configure logging in the MATTR Holder SDKs, including log levels, callback handlers, and accessing log files across iOS, Android, and React Native platforms.
The MATTR Holder SDKs include a built-in logging system that records internal SDK operations. This
is useful for debugging integration issues, monitoring SDK behavior, and capturing diagnostic
information during development and testing.
By default, SDK logs are stored locally on the device. The SDK itself does not transmit logs to any
external service, although your application can choose to export or forward log events if you
register a callback handler.
## What information the SDK can log [#what-information-the-sdk-can-log]
The SDK can log information about its internal operations, including errors and warnings encountered during SDK operations.
All log entries include the log level and a descriptive message. This information helps you
diagnose issues and understand how the SDK operates within your application.
## How SDK logs differ from activity logs [#how-sdk-logs-differ-from-activity-logs]
The SDK provides two types of logs: SDK logs and activity logs. While both are valuable for monitoring and diagnostics, they serve different purposes and audiences.
| Feature | SDK logs | Activity logs |
| ----------------- | -------------------------------------------------- | ------------------------------------ |
| **Purpose** | Developer diagnostics and debugging | User-facing credential usage history |
| **Audience** | Developers integrating the SDK | End users of your wallet application |
| **Content** | Internal SDK operations, errors, and state changes | Credential and presentation events |
| **Configuration** | `loggerConfiguration` | `activityLogConfiguration` |
To learn about activity logs, see [Configure activity logs](/docs/holding/sdk-operations/activity-log).
## Log levels [#log-levels]
The SDK supports the following log levels, ordered from most to least verbose:
| Level | Description |
| --------- | -------------------------------------------------------------------------- |
| `Verbose` | Fine-grained informational events, most detailed output. |
| `Debug` | Detailed information useful during development. |
| `Info` | General informational messages about SDK operations. |
| `Warning` | Potentially harmful situations or unexpected behavior (`Warn` in Android). |
| `Error` | Error events that might still allow the SDK to continue running. |
| `Assert` | Severe error events that indicate a critical failure (Android only). |
| `Off` | Disables logging entirely. |
The SDK uses the configured log level as a threshold: only log events at the specified level and
less verbose (more severe) are recorded. For example, setting the level to `Info` captures `Info`, `Warning`, `Error`, and `Assert`
events, but not `Debug` or `Verbose`.
## Configure logging at initialization [#configure-logging-at-initialization]
You can configure logging behavior by passing a `loggerConfiguration` object to the SDK's
`initialize` method. This configuration accepts two separate log levels:
* **`logLevel`**: Controls which log events are written to the log file.
* **`callbackLogLevel`**: Controls which log events trigger the optional callback function.
```swift title="Configure logging during initialization"
try await mobileCredentialHolder.initialize(
loggerConfiguration: LoggerConfiguration( // [!code highlight]
logLevel: .Info, // [!code highlight]
callbackLogLevel: .Warning // [!code highlight]
) // [!code highlight]
)
```
* `logLevel`: Sets the minimum level for writing log entries to the log file. Defaults to `.Off`.
* `callbackLogLevel`: Sets the minimum level for invoking the callback closure. Defaults to `.Off`.
Refer to the
[`LoggerConfiguration`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/loggerconfiguration)
reference documentation for additional details.
```kotlin title="Configure logging during initialization"
mobileCredentialHolder.initialize(
loggerConfiguration = Logger.LoggerConfiguration(
logLevel = Logger.LogLevel.INFO,
callbackLogLevel = Logger.LogLevel.WARN
),
// ...
)
```
* `logLevel`: Sets the minimum level for writing log entries to the log file and Logcat. Defaults to `Logger.LogLevel.OFF`.
* `callbackLogLevel`: Sets the minimum level for invoking the callback function. Defaults to `Logger.LogLevel.OFF`.
* `logDir`: Optional. Specifies a local directory to store log files. If not provided, logs won't be stored to file.
Refer to the
[`Logger.LoggerConfiguration`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.util/-logger/-logger-configuration/index.html)
reference documentation for additional details.
```ts title="Configure logging during initialization"
import { LogLevel } from "@mattrglobal/mobile-credential-holder-react-native"
const initializeResult = await mobileCredentialHolder.initialize({
loggerConfiguration: { // [!code highlight]
logLevel: LogLevel.Info, // [!code highlight]
callbackLogLevel: LogLevel.Warn, // [!code highlight]
}, // [!code highlight]
})
if (initializeResult.isErr()) {
const { error } = initializeResult
// handle error scenarios
return
}
```
* `logLevel`: Sets the minimum level for writing log entries to the log file and console. Defaults to `LogLevel.Off`.
* `callbackLogLevel`: Sets the minimum level for invoking the callback function. Defaults to `LogLevel.Off`.
* `logDir`: Optional. Specifies a directory to store log files. On iOS, a default directory is used. On Android, logs won't be stored to file if not provided.
Refer to the
[`LoggerConfiguration`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/types/LoggerConfiguration.html)
reference documentation for additional details.
## Handle log events with a callback [#handle-log-events-with-a-callback]
You can register a callback function during initialization to receive log events in real time. This
allows your application to process log events as they occur, for example to forward them to a custom
logging service, display them in a debug console, or filter specific events for monitoring.
The callback is only invoked for log events at or above the `callbackLogLevel` threshold.
```swift title="Register a logging callback"
try await mobileCredentialHolder.initialize(
loggerConfiguration: LoggerConfiguration(
logLevel: .Info,
callbackLogLevel: .Warning,
callback: { logEvent in // [!code highlight]
print("[\(logEvent.level)] \(logEvent.message)") // [!code highlight]
} // [!code highlight]
)
)
```
The callback receives a
[`LogEvent`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/logevent)
object with the following properties:
* `level`: The [`LogLevel`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/loglevel) of the event.
* `message`: A string describing the log event.
```kotlin title="Register a logging callback"
mobileCredentialHolder.initialize(
loggerConfiguration = Logger.LoggerConfiguration(
logLevel = Logger.LogLevel.INFO,
callbackLogLevel = Logger.LogLevel.WARN,
callback = { priority, tag, message, throwable -> // [!code highlight]
Log.d("HolderSDK", "[$tag] $message") // [!code highlight]
} // [!code highlight]
),
// ...
)
```
The callback function receives the following parameters:
* `priority`: An integer representing the log priority level.
* `tag`: An optional string tag identifying the log source.
* `message`: A string describing the log event.
* `throwable`: An optional `Throwable` associated with the log event (for error-level logs).
```ts title="Register a logging callback"
import { LogLevel } from "@mattrglobal/mobile-credential-holder-react-native"
const initializeResult = await mobileCredentialHolder.initialize({
loggerConfiguration: {
logLevel: LogLevel.Info,
callbackLogLevel: LogLevel.Warn,
callback: (log) => { // [!code highlight]
console.log(`[${log.logLevel}] ${log.tag ?? ""}: ${log.message ?? ""}`) // [!code highlight]
}, // [!code highlight]
},
})
if (initializeResult.isErr()) {
const { error } = initializeResult
// handle error scenarios
return
}
```
The callback receives a log object with the following properties:
* `logLevel`: The [`LogLevel`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/enums/LogLevel.html) of the event.
* `message`: An optional string describing the log event.
* `tag`: An optional string tag identifying the log source.
## Access the log file [#access-the-log-file]
The SDK writes log entries to a file that you can access for debugging and diagnostics. The log file
contains entries from the previous two calendar days.
```swift title="Get the log file path"
let logFilePath = mobileCredentialHolder.getCurrentLogFilePath()
// If using an app group (e.g. for app extensions):
let logFilePath = mobileCredentialHolder.getCurrentLogFilePath(appGroup: "group.com.yourcompany.app")
```
The
[`getCurrentLogFilePath`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/getcurrentlogfilepath\(appgroup:\))
method returns the file path as a string, or `nil` if no log file is available.
* `appGroup`: Optional. Specify an application group to retrieve logs generated by the SDK in an app
extension. If not provided, the SDK returns the log file from the default location.
To access the log contents, read the file at the returned path into `Data`, then decode that data
into a `[String]`, where each element represents a single log message.
```kotlin title="Get the log file path"
val logFilePath = mobileCredentialHolder.getLog()
```
The
[`getLog`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/get-log.html)
method returns the full path of the log file (with a `.log` extension), or `null` if no log file is
available.
The file contains log entries from the previous two calendar days.
```ts title="Get the log file path"
const logFilePath = await mobileCredentialHolder.getCurrentLogFilePath()
// If using an app group (iOS only):
const logFilePath = await mobileCredentialHolder.getCurrentLogFilePath({
appGroup: "group.com.yourcompany.app",
})
```
The
[`getCurrentLogFilePath`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/getCurrentLogFilePath.html)
method returns a `Promise` resolving to the log file path string, or `null` if no log file is
available.
* `appGroup`: Optional (iOS only). Specify an application group to retrieve logs generated by the
SDK in an app extension.
# SDK Tethering
URL: /docs/holding/sdk-operations/sdk-tethering
Description: Learn how MATTR Holder SDKs are tethered to a MATTR VII tenant through Holder Application configuration, enabling capabilities like Wallet Attestation.
SDK Tethering ties each SDK/app instance to a MATTR VII tenant. Tethering establishes a trust
relationship between your mobile application and the MATTR VII tenant, enabling the following
capabilities:
* **Operational insights**: View details about registered and active app instances directly from
your tenant.
* **Wallet Attestation**: Enable issuers to verify wallet integrity before issuing credentials.
See [Wallet Attestation](/docs/holding/credential-claiming-guides/wallet-attestation) for more
information.
* **Remote management channel**: SDK Tethering establishes a channel that we expect to extend in
the future with capabilities such as remote syncing of trusted issuer lists and eventing.
SDK Tethering is available from the following SDK versions:
* **iOS Holder SDK**: 6.0.0
* **Android Holder SDK**: 7.0.0
* **React Native Holder SDK**: 10.0.0
SDK Tethering is currently **optional**. You enable it by providing a platform configuration when
initializing the SDK. If you initialize the SDK without a platform configuration, the SDK skips
registration and tethering, and capabilities such as Wallet Attestation are unavailable. We expect
to make SDK Tethering **required** in an upcoming release, so we recommend configuring it now to
prepare.
## How it works [#how-it-works]
The tethering process involves three steps:
1. **Configure a Holder Application on your MATTR VII tenant**: You register your mobile app by
creating a Holder Application, identified by the bundle identifier (iOS) or the package
fingerprint (Android).
2. **Initialize the SDK with your tenant details**: When you initialize the SDK in your app, you
pass the details of the MATTR VII tenant and the Holder Application you configured on it.
3. **Automatic communication**: Once initialized, instances of your app will automatically
communicate with the configured MATTR VII tenant and retrieve the required tokens to operate
and make requests to the tenant when required.
## Token validity and offline use [#token-validity-and-offline-use]
The tokens issued during this process have configurable validity periods controlled by the
`maxTimeOfflineInSecs` field on your Holder Application configuration. This means your app can
function without internet connectivity to meet different use cases:
* **Minimum**: 1 day (86400 seconds)
* **Maximum**: 30 days (2592000 seconds)
* **Default**: 7 days (604800 seconds)
When the license token expires, the SDK must reconnect to the MATTR VII tenant to renew it.
## Configuring SDK Tethering [#configuring-sdk-tethering]
### Configure Holder Applications [#configure-holder-applications]
SDK Tethering is optional. To enable it, create a Holder Application on your MATTR VII tenant for
each platform target (iOS and Android). This is a one-time setup process that registers your app
with the tenant and allows app instances to obtain the necessary tokens for authentication and operation.
Make a request of the following structure to create an iOS Holder Application configuration on your MATTR VII tenant:
```http title="Request"
POST /v1/holder/applications
```
```json title="Request body"
{
"name": "My iOS Holder Application",
"clientId": "my-wallet-client",
"type": "ios",
"bundleId": "com.yourcompany.holderapp",
"teamId": "YOUR_APPLE_TEAM_ID",
"maxTimeOfflineInSecs": 864000,
"appAttest": {
"required": true,
"environment": "production"
}
}
```
* `name`: A unique name to identify this Holder Application.
* `clientId`: OAuth 2.0 `client_id` value that the holder application uses when requesting client
attestations. This value is included as the `sub` claim in attestation JWTs and must match the
`client_id` configured by issuers who trust this Holder Application.
* `type`: Must be `ios`.
* `bundleId`: The Bundle ID of your iOS app (must match your Xcode project configuration).
* `teamId`: Your Apple Developer Team ID.
* `maxTimeOfflineInSecs`: Maximum number of seconds the SDK can operate offline before requiring
a new license token. Must be between 1 day (86400) and 30 days (2592000). Defaults to 7 days
(604800).
* `appAttest`: App Attest configuration for the iOS holder application:
* `required`: When `true`, the app instance must provide a valid App Attest attestation during
registration and token renewal. When `false`, the app can fall back to assertion-only
authentication. See [Attestation vs Assertion](/docs/holding/sdk-operations/sdk-tethering#attestation-vs-assertion-fall-back) for more details.
* `environment`: The App Attest environment (`development` or `production`). Apple recommends
using `development` for testing and `production` for distribution builds.
A successful response returns a `201` status code with the created Holder Application:
```json title="Response"
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c", // [!code focus]
"name": "My iOS Holder Application",
"clientId": "my-wallet-client",
"type": "ios",
"bundleId": "com.yourcompany.holderapp",
"teamId": "YOUR_APPLE_TEAM_ID",
"maxTimeOfflineInSecs": 864000,
"appAttest": {
"required": true,
"environment": "production"
}
}
```
* `id`: A unique identifier for the Holder Application (generated by the tenant). You must use this value when initializing the SDK so that it can correctly
identify and authenticate your application.
Make a request of the following structure to create an Android Holder Application configuration on your MATTR VII tenant:
```http title="Request"
POST /v1/holder/applications
```
```json title="Request body"
{
"name": "My Android Holder Application",
"clientId": "my-wallet-client",
"type": "android",
"packageName": "com.yourcompany.holderapp",
"packageSigningCertificateThumbprints": [
"1232584B6F6A892D356899FB9576C5F226A179E6199F2B7A1D837B5C234C5A8E"
],
"maxTimeOfflineInSecs": 864000,
"keyAttestation": {
"required": true
}
}
```
* `name`: A unique name to identify this Holder Application.
* `clientId`: OAuth 2.0 `client_id` value that the holder application uses when requesting client
attestations. This value is included as the `sub` claim in attestation JWTs and must match the
`client_id` configured by Issuers who trust this Holder Application.
* `type`: Must be `android`.
* `packageName`: The package name of your Android application.
* `packageSigningCertificateThumbprints`: SHA-256 hex-encoded fingerprints of the signing key
certificates used to sign your APK or app bundle. This ensures the tenant only accepts requests
from known and trusted applications. Refer to
[Android app signing](/docs/holding/android-app-signing) for more information.
* `maxTimeOfflineInSecs`: Maximum number of seconds the SDK can operate offline before requiring
a new license token. Must be between 1 day (86400) and 30 days (2592000). Defaults to 7 days
(604800).
* `keyAttestation`: Key Attestation configuration for the Android holder application:
* `required`: When `true`, the app instance must provide a valid Key Attestation during
registration and token renewal. When `false`, the app can register and renew tokens using
just an authentication assertion. See [Attestation vs Assertion](/docs/holding/sdk-operations/sdk-tethering#attestation-vs-assertion-fall-back) for more details.
A successful response returns a `201` status code with the created Holder Application:
```json title="Response"
{
"id": "a82bfa46-72a0-4cde-b6cb-2a0de7e2f3c4", // [!code focus]
"name": "My Android Holder Application",
"clientId": "my-wallet-client",
"type": "android",
"packageName": "com.yourcompany.holderapp",
"packageSigningCertificateThumbprints": [
"1232584B6F6A892D356899FB9576C5F226A179E6199F2B7A1D837B5C234C5A8E"
],
"maxTimeOfflineInSecs": 864000,
"keyAttestation": {
"required": true
}
}
```
* `id`: A unique identifier for the Holder Application (generated by the tenant). You must use this value when initializing the SDK so that it can correctly
identify and authenticate your application.
For React Native applications, you must create **both** an iOS and an Android Holder Application
on your MATTR VII tenant, and then conditionally pass the correct configuration based on the
platform OS at runtime.
**Step 1: Create the iOS Holder Application**
Follow the instructions in the **iOS** tab to create a Holder Application configuration for iOS.
**Step 2: Create the Android Holder Application**
Follow the instructions in the **Android** tab to create a Holder Application configuration for Android.
**Step 3: Pass the correct configuration based on Platform OS**
When initializing the SDK, use `Platform.OS` to conditionally provide the matching Holder
Application configuration:
```typescript title="Example"
import { Platform } from "react-native";
const holderApplicationId =
Platform.OS === "ios"
? "YOUR_IOS_HOLDER_APPLICATION_ID"
: "YOUR_ANDROID_HOLDER_APPLICATION_ID";
```
* `YOUR_IOS_HOLDER_APPLICATION_ID` : The `id` returned when you created the iOS Holder Application.
* `YOUR_ANDROID_HOLDER_APPLICATION_ID` : The `id` returned when you created the Android Holder Application.
### Initialize the SDK with platform configuration [#initialize-the-sdk-with-platform-configuration]
If you are using SDK Tethering, update your SDK initialization to include the platform
configuration once your Holder Applications are created. This enables your app to connect to the
correct MATTR VII tenant and Holder Application. If you are not using tethering, you can initialize
the SDK without a `platformConfiguration`.
Initialize the SDK with your platform configuration:
```swift title="Initialization"
let platformConfig = PlatformConfiguration(
tenantHost: URL(string: "https://your-tenant.vii.mattr.global")!,
applicationId: "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
)
try await MobileCredentialHolder.shared.initialize(
platformConfiguration: platformConfig
)
```
* `tenantHost`: The URL of your MATTR VII tenant. This must be the tenant where your iOS Holder
Application is configured.
* `applicationId`: The `id` of your configured iOS Holder Application.
Initialize the SDK with your platform configuration:
```kotlin title="Initialization"
val platformConfig = PlatformConfiguration(
tenantHost = URL("https://your-tenant.vii.mattr.global"),
applicationId = "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
)
MobileCredentialHolder.initialize(context, platformConfiguration = platformConfig)
```
* `tenantHost`: The URL of your MATTR VII tenant where your Android Holder Application is configured.
* `applicationId`: The `id` of your configured Android Holder Application.
Since React Native bridges both iOS and Android, and each platform has its own Holder Application
registered on your MATTR VII tenant (see
[Create a Holder Application](/docs/holding/sdk-getting-started#create-a-holder-application)), your
initialization code must pass the correct platform-specific `applicationId` at runtime. Use
`Platform.OS` to select the appropriate value:
```typescript title="Initialization"
import { initialize } from "@mattrglobal/mobile-credential-holder-react-native";
import { Platform } from "react-native";
const applicationId =
Platform.OS === "android"
? "your-android-holder-application-id"
: "your-ios-holder-application-id";
await initialize({
platformConfiguration: {
tenantHost: "https://your-tenant.vii.mattr.global",
applicationId,
},
});
```
Replace the placeholder values with the `id` returned when you created each Holder Application. In
practice, you would typically store these values in a configuration file or environment variables.
Once your Holder Application configurations are created, your application will be able to use the SDK and
interact with the MATTR VII platform (for example, to obtain attestation tokens).
## Managing application instances [#managing-application-instances]
Once your Holder Application is configured and the SDK is initialized, each device that launches
your app registers as a new application instance on your tethered MATTR VII tenant. You can view and manage
these instances via the MATTR VII API.
### Retrieve all registered instances [#retrieve-all-registered-instances]
To view all registered instances for a Holder Application and track usage:
```http title="Request"
GET /v1/holder/applications/{applicationId}/instances
```
* `applicationId` : The `id` of the Holder Application you want to inspect.
The response includes a paginated list of all registered instances:
```json title="Response"
{
"data": [
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c",
"appAttestationType": "app_attestation",
"registeredAt": "2023-10-05T14:48:00.000Z",
"licenseExpiresAt": "2024-10-05T14:48:00.000Z",
"lastAttestedAt": "2023-12-01T10:30:00.000Z",
"externalReferenceId": "external-ref-12345",
"deviceDetails": {
"deviceModel": "iPhone 12",
"deviceMake": "Apple",
"osVersion": "iOS 14.4"
},
"sdkDetails": {
"sdkVersion": "1.2.3"
}
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
Each instance includes:
* `id` : Unique identifier for the registered instance.
* `appAttestationType` : The type of attestation used during registration (`none`,
`app_attestation`, or `key_attestation`).
* `registeredAt` : When the instance was first registered.
* `licenseExpiresAt` : When the instance's license expires (the Holder SDK will automatically handle license renewal).
* `lastAttestedAt` : When the instance was last attested.
* `deviceDetails` : Information about the device (model, make, OS version).
* `sdkDetails` : Information about the SDK version used by the instance.
This is useful for tracking how many devices are actively using your application and monitoring usage quotas.
### Delete a specific instance [#delete-a-specific-instance]
To remove a specific registered instance:
```http title="Request"
DELETE /v1/holder/applications/{applicationId}/instances/{instanceId}
```
* `applicationId` : The `id` of the Holder Application.
* `instanceId` : The `id` of the specific instance to delete.
Once deleted, the instance can no longer interact with the platform or receive tokens, and any
existing tokens are revoked.
Deleting instances is primarily useful during **testing** when you have a limited number of devices
and need to re-register a fresh instance (for example, to test the initial registration flow again).
In production, there is nothing preventing the application from requesting another token on the next
launch, which would create a new instance — so deleting instances is not an effective way to block
a device.
## Attestation vs Assertion fall-back [#attestation-vs-assertion-fall-back]
When configuring a Holder Application, you control whether your MATTR VII tenant requires
**attestation** (hardware-backed proof of app integrity) or also accepts a lighter-weight
**assertion** (a cryptographic signature proving key possession) during instance registration and
token renewal.
Each platform has an attestation configuration with a `required` boolean:
* When `required` is `true`, the app instance must provide a valid attestation during registration
and token renewal.
* When `required` is `false`, your tenant also accepts an assertion when an attestation is not
available.
The SDK handles this automatically. It always attempts to provide an attestation, and falls back to
an assertion if it cannot generate one (for example, when the platform attestation service is
temporarily unavailable). Your tenant then accepts or rejects the request based on the `required`
setting. Your application does not need to manage attestation or assertion details directly.
### When to use each setting [#when-to-use-each-setting]
| Scenario | Recommended setting |
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| **Production apps in distribution** | `required: true` — Provides the strongest integrity guarantees by verifying the app and device through OS-level attestation. |
| **Development and testing** | `required: false` — Useful when running on simulators or devices where attestation services are unavailable. |
| **Broad device compatibility** | `required: false` — Some older devices may not support hardware attestation. The assertion fall-back ensures these devices can still register. |
Setting attestation to `required: false` reduces the security guarantees of the tethering
process. Only use this setting when you have a specific need, such as supporting older devices
or during development.
### Certificate expiry and required Key Attestation (Android) [#certificate-expiry-and-required-key-attestation-android]
This behavior is specific to **Android Key Attestation**. It does not apply to iOS App Attest.
When Key Attestation is set to required, an attestation must be present, parseable, and trusted (a
trusted root with each certificate in the chain signed by the one above it) for registration to
succeed.
One thing to be aware of: our validation does not check whether the certificates in the attestation
chain have expired. Some Android devices, including relatively recent models, generate attestations
where part or all of the certificate chain is already past its validity period. To avoid blocking
these devices, an otherwise valid attestation with expired certificates is still treated as a
successful attestation, even when Key Attestation is set to required.
In practice this means a device presenting an expired (but otherwise valid) attestation chain will
register successfully and be marked as attested. This behavior may be tightened in a future release,
so we recommend not relying on certificate expiry as part of your own trust decision.
# API Reference
URL: /docs/issuance/authorization-code/api-reference
## Create Credential Offer [#create-credential-offer]
## Request authorization for access to resources [#request-authorization-for-access-to-resources]
## Exchange authorization code for access token [#exchange-authorization-code-for-access-token]
## Issue a verifiable credential [#issue-a-verifiable-credential]
## Retrieve issuer metadata [#retrieve-issuer-metadata]
# OID4VCI Authorization Code flow journey pattern
URL: /docs/issuance/authorization-code/journey-pattern
This journey pattern is used to issue credentials of different formats to a holder via the OID4VCI
[Authorization Code](/docs/issuance/authorization-code/overview) flow.
## Overview [#overview]
* **Issuance channel**: Remote, Unsupervised
* **Device/s**: On-device / Cross-device / in-person
* **Formats**: mDocs, CWT, Semantic CWT
* **Information assurance level**: High
* **Identity assurance level**: High (when identity assurance checks are included)
## Journey flow [#journey-flow]
## Architecture [#architecture]
### Scanning the QR code [#scanning-the-qr-code]
The QR code that is used to initiate the issuance workflow is created by the Issuer, but the Holder
selects when to scan it using their digital wallet (1) which triggers the issuance workflow.
This QR code can be sent to the holder via any existing communication channels, including digital
and paper based channels.
### Credential offer [#credential-offer]
Once the QR code is scanned it will result in the wallet displaying the credential offer that was
created by the Issuer using MATTR VII issuance capabilities. The offer details the credential
formats that will be issued in this workflow, and what details would each credential include.
Digital Trust Service capabilities (9) enable creating and maintaining policies that define what
Issuers can be trusted and what credential types they are allowed to issue. This introduces an
additional level of trust to interactions within the trust network, making it easier for Samantha to
decide whether or not she wishes to claim a credential from this Issuer.
### Obtaining a binding attribute [#obtaining-a-binding-attribute]
The OpenID Credential Provisioning (2) component commences the credential issuance flow by obtaining
a unique binding attribute from the requesting device/wallet. This happens when the user accepts the
credential offer (step 3 in the pattern above).
The binding attribute is carried through the proceeding steps to bind the intended credential holder
and the data.
### Authentication [#authentication]
The OpenID Connect Provider (IdP) is a component managed by the Issuer to authenticate users against
the data they hold about them. The Issuer’s IdP (3) facilitates the authentication flow (step 4 in
the pattern above) which may include multiple steps that test different factors such as:
* Basic username/password authentication.
* Proving device ownership through acknowledging a unique request sent directly to the device.
* Providing genuine presence assurance (liveness check) that the correct user is in possession of
the device being used to facilitate the journey.
The issuer can configure this authentication flow requests to include login hint parameters (for
example pre populating the user e-mail address) to create more seamless user experiences.
### Interaction hook [#interaction-hook]
Following successful authentication, the user can be redirected to a custom component (4) hosted or
controlled by the Issuer (step 5 in the pattern above). This custom component can initiate
additional steps for the user to perform as part of issuing the credential.
This can be used to embed enrolment within the issuance journey.
We refer to these custom components as *Interaction hooks*.
### Claims source [#claims-source]
As part of gathering authenticated claims that are included in the issued credential, the Issuer may
want to retrieve additional information about the user from external data store/s (5).
This optional step (step 6 in the pattern above) enables issuing credentials that are more rich in
information and thus provide greater value. It is achieved by configuring an external data store,
which we refer to as a *Claims source*.
### Credential generation [#credential-generation]
The information then gets passed back through the OpenID Credential Provisioning component (2) to
map against an established vocabulary, and express the intended context around each piece of
information it holds.
The mapped data is then passed to the Credential Generation component (6) which formats, binds and
signs the data into a credential that is ready to be sent to the requesting wallet/device (step 7 in
the pattern above).
The credential can be issued in multiple-formats. Additional features may be enabled to support
capabilities such as credential revocation or allowing the holder to respond to selective-disclosure
verification requests.
### Credential management [#credential-management]
Digital wallets (1) can be used to manage the acceptance and secure storage of the credential on the
Holder’s device upon completion of the credential issuance flow (step 8 in the pattern above). This
can be achieved by wallets built with our MATTR Pi Wallet SDK or branded MATTR GO Hold applications.
### Webhooks [#webhooks]
At the completion of the issuance flow, MATTR VII will trigger any configured Webhook events to
configured recipients (7). These events (step 9 in the pattern above) offer additional information
about the credential issuance (such as the wallet DID) back to the issuer for them to utilize or
store.
This enables integrating issuance workflows into existing business processes, or creating new ones
based on this capability.
# OID4VCI Authorization Code workflow
URL: /docs/issuance/authorization-code/overview
## Overview [#overview]
The Authorization Code flow is an interactive, user-centric flow where the credential recipient
(usually a wallet) redirects the user to authenticate with the issuer (e.g., a government or
organization). After consent and authentication, the wallet exchanges an authorization code for a
credential.
This flow is ideal for scenarios where the user needs to provide explicit consent and may require
additional authentication steps. It is also useful when the issuer needs to gather additional
information from the user or perform additional checks before issuing the credential.
## Workflow [#workflow]
The following diagram depicts the OID4VCI Authorization Code flow:
### Creating a Credential offer [#creating-a-credential-offer]
The workflow begins when an issuer creates what is referred to as a
[credential offer](/docs/issuance/credential-offer/overview), used to share important information
with the holder’s digital wallet. This includes the credential’s issuer, what it includes and how to
claim it. The OID4VCI specification defines a method for a digital wallet to discover this
information in a standardized and interoperable way.
### Sharing a Credential offer [#sharing-a-credential-offer]
Credential offers are created by making a request to a MATTR VII endpoint that returns an offer URI.
This URI is shared with the intended holder as a QR code, deep-link or wallet push notification.
The offer only includes metadata required to initiate the issuance process, and the actual
credential is only created and signed later in the workflow.
Refer to [Claiming credential offers](/docs/issuance/credential-offer/overview#claiming-credential-offers) for more details on how to adjust the offer URI to use specific schemes and the resulting user experience.
### Accepting a Credential offer [#accepting-a-credential-offer]
After receiving the credential offer URI, the wallet uses it to retrieve required metadata from the `.well-known` endpoints:
```http title="Issuer Metadata Endpoint"
https://{your_tenant_url}/.well-known/openid-credential-issuer
```
```http title="Authorization Server Metadata Endpoint"
https://{your_tenant_url}/.well-known/oauth-authorization-server
```
As required by the OID4VCI specification, these endpoints must be publicly accessible. They provide the information necessary to initiate the issuance process, including the issuer’s details, the types of credentials available, and the relevant endpoints and supported parameters used throughout the workflow.
The wallet then presents relevant information from the credential offer to the holder and requests their consent to proceed. Upon receiving consent, the wallet initiates the Authorization Code flow, redirecting the holder to the issuer’s authorization endpoint to authenticate and authorize the credential issuance.
**Reusable offers**: Authorization Code flow offers can be claimed multiple times by different users. Since each user authenticates independently during the credential issuance workflow, the same offer URI can be used to issue credentials to multiple holders. Each holder will receive a credential containing their own user-specific claims, gathered during their individual issuance session.
This makes Authorization Code offers suitable for scenarios like:
* QR codes displayed publicly (e.g., at an event registration desk)
* Links shared with multiple recipients (e.g., an email to a group)
* Self-service credential issuance portals
### Authentication [#authentication]
When the holder consents they are redirected to a configured
[Authentication provider](/docs/issuance/authorization-code/authentication-provider/overview), used to authenticate and
identify the holder. This provider can also be used as an identity provider to retrieve claims about
the authenticated user. Once retrieved, these claims can be used in the issued credential.
After the holder completes authentication, they are redirected from the Authentication provider back
to the issuance workflow.
Inline with the [OID4VCI
specification](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-token-endpoint),
this step includes receiving a `code` from the Authentication provider upon successful
authentication and exchanging it with a Token that is used later in the workflow to issue the
credential.
### Custom components (*optional*) [#custom-components-optional]
This is where issuers can *optionally* add their own business logic to the workflow by configuring
an [Interaction hook](/docs/issuance/authorization-code/interaction-hook/overview). This is *optional* step enables
issuers to plug in any custom experiences to interact with the holder after they have authenticated
but before the credential is issued. For example, this can be used to gather additional information
from the user or to add higher identity assurance components such as a liveness or biometrics
checks.
### Querying an external database (*optional*) [#querying-an-external-database-optional]
Following the interaction hook, issuers can configure another *optional* step to retrieve additional
user claims from an existing databases and use it in the issued credential. This integration is
referred to as a [Claims source](/docs/issuance/claims-source/overview). Claims sources can query the
configured endpoints with any of the user claims retrieved from the Authentication provider and/or
Interaction hook. This enables the issuer to create flexible and efficient queries to retrieve the
exact user claims required to issue a specific credential.
### Formatting and Signing the credential [#formatting-and-signing-the-credential]
Now that all the data is available, MATTR VII will format it into a digital credential and
cryptographically sign it. This is achieved using a
[Credential configuration](/docs/issuance/credential-configuration/overview) that is defined as part
of the credential offer. This is a template that defines how should the issued credential be
constructed. It details where the credential claims are mapped from, what the credential should look
like in the wallet, when and if it should expire and whether it is revocable.
### Delivering the signed credential [#delivering-the-signed-credential]
Finally, the signed credential is delivered directly to the holder’s digital wallet and can be
presented to verifying parties upon request.
## Configuration [#configuration]
The OID4VCI workflow makes use of different components that can be configured either using direct
API requests to a MATTR VII tenant or via the [MATTR Portal](/docs/platform-management/portal).
* [Authentication provider](/docs/issuance/authorization-code/authentication-provider/overview) (*required*): An
authentication provider is used to store and manage user accounts on behalf of an organization or
service provider. The provider will be the first screen users see when trying to claim a
credential as part of an OID4VCI workflow. Usually this is a login page to verify user
credentials, but it could be any custom implementation as long as it follows the OpenID Connect
standard.
* [Interaction hook](/docs/issuance/authorization-code/interaction-hook/overview) (*optional*): If you would like to
integrate with any existing components beyond a login page (e.g. MFA, biometrics checks, consent
screens), you can set up an interaction hook to redirect users. You can configure the interaction
hook to pass back additional user claims or modify existing ones. The interaction hook enables
adding additional steps to the credential-claiming journey by bouncing users to your own web
service.
* [Claims source](/docs/issuance/claims-source/overview) (*optional*): Usually authentication
providers do not store all the information about a user and only keep attributes like email, names
or other short identifiers. When issuing credentials, you will likely need more user information
and to accommodate that, the workflow enables retrieving data from a custom claim source via a
single API call. If you have additional user information stored in a separate database or service,
add a claims source to fetch claims directly from a compatible standalone system and use these
claims when issuing credentials.
* [Credential configuration](/docs/issuance/credential-configuration/overview) (*required*): Add your
credential types, branding, claims, and other metadata. You can also mix and match where claims
for the issued credentials come from - an authentication provider, an interaction hook, or a
claims source.
# OID4VCI Authorization Code flow quickstart guide
URL: /docs/issuance/authorization-code/quickstart
This quickstart is for evaluating MATTR’s OID4VCI Authorization Code flow issuance capabilities. In about 10-15 minutes you will configure an OID4VCI Authorization Code flow in the MATTR Portal, generate a credential offer, and claim an mDoc into the GO Hold example app.
**Estimated time: 10-15 minutes.**
Use this guide as a fast evaluation path to see the flow working end-to-end. For detailed information and API examples, explore the
[tutorial](/docs/issuance/authorization-code/tutorial) and reference documentation.
## User experience [#user-experience]
In this quickstart you will perform this exact flow yourself using the MATTR Portal, a mock authentication provider, and the GO Hold example app:
1. User scans a QR code from an issuer.
2. The wallet displays what credential is being offered.
3. The user agrees to claim the offered credential.
4. The user is redirected to complete authentication.
5. Upon successful authentication, the credential is issued to the wallet.
## Prerequisites [#prerequisites]
* MATTR VII tenant access via the [MATTR Portal](https://portal.mattr.global/). Apply for access [here](/docs/resources/get-started).
* Install the **MATTR GO Hold example app** for [iOS](https://apps.apple.com/app/mattr-wallet/id1518660243) or [Android](https://play.google.com/store/apps/details?id=global.mattr.wallet).
## Steps [#steps]
### Configure mock Authentication provider (3 minutes) [#configure-mock-authentication-provider-3-minutes]
This quickstart uses a MATTR-hosted mock authentication provider to keep setup simple. In production you will configure your own IdP (for example Auth0) and real users. See the [full tutorial](/docs/issuance/authorization-code/tutorial) for a detailed Auth0 example.
1. Log into the [MATTR Portal](https://portal.mattr.global/).
2. Switch to your tenant if you have access to multiple tenants, or [create a new tenant](/docs/platform-management/portal#creating-a-tenant) if needed.
3. Expand **Credential Issuance**.
4. Select **Authentication provider**.
5. Enter `https://learn.au.auth.mattrlabs.com/` in the **Base URL** field.
6. Enter any string in the **Client ID** and **Client Secret** fields (these are ignored by the mock Authentication provider).
7. Select **Create**.
### Create issuer certificate (2 minutes) [#create-issuer-certificate-2-minutes]
This allows your tenant to act as an issuing authority for mDocs in this demo. This step is only required if you haven't already set up an issuer certificate for your tenant. If you already have an active IACA, skip to the next step.
1. Expand **Platform Management**.
2. Select **Certificates**.
3. Select **Create new**.
4. Select **IACA - Issuing Authority Certificate Authority** as the type.
5. Select **MATTR managed** as the management method.
6. Select **Create**.
7. Set **Status** to **Active**.
8. Select **Update** to activate the certificate.
### Create mDoc credential configuration (3 minutes) [#create-mdoc-credential-configuration-3-minutes]
In this quickstart you’ll use a simple, pre-defined credential configuration with default claim values so you can issue a credential without integrating any external data sources:
1. Expand **Credential Issuance**.
2. Select **mDocs**.
3. Select **Create new**.
4. Enter a **Name** (e.g., "My First Credential").
5. Enter a **Description** (e.g., "Claimed via Authorization Code flow").
6. Enter a unique **Credential type** (e.g., `com.example.authcodecredential`).
7. Paste the following JSON into **Claim mappings**:
```json title="Claim mappings object"
{
"com.example.personaldetails.1": {
"name": {
"defaultValue": "Emma Tasma",
"type": "string"
},
"email": {
"defaultValue": "emma.tasma@example.com",
"type": "string"
}
}
}
```
8. Enter "1" in the **Months** field under **Validity for**.
9. Select **Create**.
### Generate a credential offer (2 minutes) [#generate-a-credential-offer-2-minutes]
This creates the OID4VCI offer that wallets can use to start the Authorization Code flow.
1. Expand **Credential Issuance**.
2. Select **Credential offer**.
3. Select **Authorization code flow** as the workflow.
4. Select the **Select** button.
5. Check the checkbox next to your credential configuration.
6. Select **Apply**.
7. Select **Generate**.
8. Download and save the QR code (or just leave it on the screen for scanning in the next step).
### Claim the credential (2 minutes) [#claim-the-credential-2-minutes]
Now use the GO Hold example wallet to experience the end-to-end flow from QR scan to credential in the wallet:
1. Open the [**GO Hold example app**](/docs/holding/go-hold/getting-started).
2. Select **Share** on the home screen.
3. Select **Respond or Collect** (You may need to allow the app to access your camera).
4. Scan the QR code you generated in the previous step.
5. Review the credential offer and select **Proceed**.
6. When prompted to open the authentication page, select **Continue**.
7. Select **Sign in** to complete the authentication flow (you do not need to change any of the login details).
8. You will be redirected back to the GO Hold app where you can see the issued credential.
Behind the scenes, MATTR handled the OID4VCI Authorization Code flow, including redirecting to the mock authentication provider, validating the user, and issuing the mDoc to your GO Hold example app.
Congratulations! You've successfully configured an OID4VCI Authorization Code flow and issued an mDoc to a digital wallet using only MATTR Portal configuration and the GO Hold example app.
## Next steps [#next-steps]
* For your evaluation:
* Note how long this quickstart took and any friction you encountered.
* Review the credential configuration and consider how it would map to your real data.
* Explore the [OID4VCI Authorization Code flow tutorial](/docs/issuance/authorization-code/tutorial) for detailed instructions and explanations.
* Try the [OID4VCI Pre-authorized Code flow](/docs/issuance/pre-authorized-code/quickstart) for a different issuance workflow.
# OID4VCI Authorization Code flow tutorial
URL: /docs/issuance/authorization-code/tutorial
## Introduction [#introduction]
The
[OID4VCI specification](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html)
is an open standard developed by the OpenID Foundation. It leverages the OpenID protocol to support
verifiable credentials issuance and management.
In this tutorial we will configure an
[OID4VCI Authorization Code flow](/docs/issuance/authorization-code/overview) and use it to
issue an [mDoc](/docs/concepts/mdocs) to the
[MATTR GO Hold example app](/docs/holding/go-hold/getting-started), showing each step both through the MATTR Portal and via the equivalent API calls.
## User experience [#user-experience]
This is the user experience you will build in this tutorial:
1. User launches their [GO Hold example app](/docs/holding/go-hold/getting-started) and
scans a QR code received from an issuer.
2. The wallet displays what credential is being offered to the user and by what issuer.
3. The user agrees to claiming the offered credentials.
4. The user is redirected to complete authentication via Auth0.
5. Upon successful authentication, the credential is issued to the user's GO Hold example app. They
can now view the credential and present it for verification.
## Prerequisites [#prerequisites]
* Complete the [sign up form](/docs/resources/get-started) to get trial access to MATTR VII and
the MATTR Portal, and then [Create a tenant](/docs/platform-management/portal#creating-a-tenant).
* Install the **MATTR GO Hold example app** by following the
[getting started guide](/docs/holding/go-hold/getting-started).
* Sign up with [Auth0](https://auth0.com/signup) which we will use to authenticate the holder as
part of the issuance workflow.
We recommend using the MATTR VII [Postman
collection](/docs/api-reference#postman-collection)
in this tutorial. While this isn't an explicit prerequisite it can really
speed things up.
## Tutorial overview [#tutorial-overview]
To build this user experience, the current tutorial comprises the following steps:
1. [Configure an Auth0 application](#configure-an-auth0-application): We will use this application
to authenticate the user as part of the
[OID4VCI Authorization Code flow](/docs/issuance/authorization-code/overview).
2. [Obtain a MATTR VII access token](#obtain-a-mattr-vii-access-token): You need this to access
protected endpoints on your MATTR VII tenant.
3. [Configure an Authentication provider](#configure-a-mattr-vii-authentication-provider): Required
to authenticate users before you can issue them credentials.
4. [Create Issuer certificates](#create-issuer-certificates): Required to sign mDocs.
5. [Create an mDocs credentials configuration](#create-a-mattr-vii-mdocs-credentials-configuration):
Controls the content and branding of issued Credentials.
6. [Create and share a Credential offer](#create-a-credential-offer): Used by digital wallets to
trigger the issuance workflow.
7. [Claim the credential as the holder](#use-the-mattr-go-example-app-to-claim-the-credential): Use
your GO Hold example app to claim the offered Credential.
## Tutorial steps [#tutorial-steps]
### Configure an Auth0 application [#configure-an-auth0-application]
Let's start by configuring an Auth0 application. This application will be used to authenticate the
user as part of the
[OID4VCI Authorization Code flow](/docs/issuance/authorization-code/overview).
##### Create a new Auth0 application [#create-a-new-auth0-application]
1. Log into [Auth0](https://auth0.com/).
2. Skip the Auth0 onboarding tutorials and go straight to your **Dashboard**.
3. Select **Create Application**.
4. Insert a *Name* for your application.
5. Select **Regular Web Application**.
6. Select **Create**.
7. Select **Skip Integration**.
Your application is created and you will be redirected to the **Settings** tab under the
**Applications** section.
8. Record your application `Domain`, `Client ID` and `Client Secret`.
9. Add a simple **Description**.
10. Scroll down to the *Application URIs* area, locate the *Allowed Callback URLs* textbox and
insert the following URL: `https://{your_tenant_url}/core/v1/oauth/authentication/callback`
(make sure you replace `{your_tenant_url}` with the `tenant_url` provided with your MATTR VII
[tenant details](/docs/platform-management/portal#getting-started)).
11. Select the **Connections** tab.
12. Enable *Username-Password-Authentication* under **Database**.
13. Disable everything under **Social**.
##### Create a User [#create-a-user]
1. Select the **User Management** menu on the left hand side navigation panel and select **Users**.
2. Select the **Create User** button.
3. Add an *Email*. This must be different to the one you use to sign up to your Auth0 account.
4. Add a *password*.
5. Select **Username-Password-Authentication** from the *Connection* dropdown list.
6. Select **Create**. You will be redirected to the new user’s *Details* tab.
7. Select **Edit** under *Name* and replace the value (Auth0 uses the email by default) with the
full user name.
Auth0 holds and stores information against user objects, **including PII**. Before you add any
additional data, make sure you understand
[Auth0's policies](https://auth0.com/docs/users/normalized/auth0/store-user-data) around storing
this information.
### Configure a MATTR VII Authentication provider [#configure-a-mattr-vii-authentication-provider]
Next you need to configure your Auth0
[Authentication provider](/docs/issuance/authorization-code/authentication-provider/overview) in your MATTR VII tenant.
This is required so that MATTR VII can use the Auth0 application to authenticate users before
issuing them credentials.
1. In the navigation panel on the left-hand side, expand the **Credential Issuance** menu.
2. Select **Authentication provider**.
3. Insert your Auth0 application `Domain` in the *Base URL* field. Make sure you prefix it with
`https://`.
4. Insert your Auth0 application `Client ID` in the *Client ID* field.
5. Insert your Auth0 application `Client Secret` in the *Client secret* field.
6. Select **Create**.
**Step 1: Obtain a MATTR VII access token**
All of the MATTR VII endpoints you will use in this tutorial
are protected, so you will first need to use your [tenant details](/docs/platform-management/portal#getting-started) to make a request of the following
structure and obtain an access token:
```http title="Request"
POST https://{auth_server}/oauth/token
```
* `auth_server` : Replace with the `auth_url` value from your tenant details.
```json title="Request body"
{
"client_id": "F5qae****************************",
"client_secret": "Wzc8J**********************************************************",
"audience": "learn.vii.au01.mattr.global",
"grant_type": "client_credentials"
}
```
* Replace `client_id`, `client_secret` and `audience` with your own tenant details.
* Keep `grant_type` as `client_credentials`.
*Response*
```json title="Response body"
{
"access_token": "eyJhb********************************************************************",
"expires_in": 14400,
"token_type": "Bearer"
}
```
Use the returned `access_token` value as a bearer token for all requests to your MATTR VII tenant in
the next steps of this tutorial.
You will need to obtain a new access token whenever it expires. We recommend
using our [Postman
collection](/docs/api-reference#postman-collection)
to interact with your MATTR VII tenant. This collection includes a pre-request
script to obtain an access token when it is missing or expired, as well as
pre-configured templates for all available requests.
**Step 2: Configure an Authentication provider**
Now that you have an access token, you can make a request to create an
[Authentication provider configuration](/docs/issuance/authorization-code/authentication-provider/overview) based on the
details of your Auth0 application. This will be used to authenticate users as part of the
[OID4VCI Authorization Code flow](/docs/issuance/authorization-code/overview).
Make the following API request to your MATTR VII tenant:
```http title="Request"
POST /v1/users/authentication-providers
```
```json title="Request body"
{
"url": "https://example.us.auth0.com/",
"scope": ["openid", "profile", "email"],
"clientId": "zH1IGGkRo6q0ofwiNJnlY0bg9fchw3s0",
"clientSecret": "***********************************************************LFRrZ",
"tokenEndpointAuthMethod": "client_secret_post",
"staticRequestParameters": {
"prompt": "login",
"maxAge": 10000
}
}
```
* `url` : Replace with your Auth0 application `Domain`. Make sure you prefix it with `https`.
* `scope` : This determines what claims are returned by the authentication provider following
successful authentication. Refer to
[Auth0 OIDC scopes](https://auth0.com/docs/get-started/apis/scopes/openid-connect-scopes) for more
information.
* `clientId` : Replace with your Auth0 application `Client ID`.
* `clientSecret`: Replace with your Auth0 application `Client Secret`.
### Create issuer certificates [#create-issuer-certificates]
In this tutorial you are going to issue an [mDoc](/docs/concepts/mdocs), so you need to have valid
[IACA](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca).
1. Log into the [MATTR Portal](https://portal.mattr.global/).
2. In the navigation panel on the left-hand side, expand the **Platform Management** menu.
3. Select **Certificates**.
4. Select the **Create new** button.
5. Use the *Type* radio button to select **IACA - Issuing Authority Certificate Authority**.
6. Use the *Management method* radio button to select **MATTR managed**.
7. Use the *Country* dropdown list to select an issuing country.
8. Select the **Create** button to create the IACA certificate.\
The IACA is created as *inactive* by default.
9. Use the *Status* radio button to select **Active**.
10. Select the **Update** button to activate the IACA certificate.
1. Make the following request to your MATTR VII tenant to create an
[IACA](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca):
```http filename:"Request"
POST /v2/credentials/mobile/iacas
```
The response will include an `id` element which you will use in the next step.
2. Make the following request to activate the IACA you just created:
```http filename:"Request"
PUT /v2/credentials/mobile/iacas/{iacaId}
```
* `iacaId`: Replace with the `id` element returned in the response when you created the IACA in the
previous steps.
```http filename:"Request body"
{
"active": true
}
```
Your IACA is now active and can be used to sign mDocs.
### Create a MATTR VII mDocs credential configuration [#create-a-mattr-vii-mdocs-credential-configuration]
Now that you have valid certificates in place, the next component you need is a
[Credential configuration](/docs/issuance/credential-configuration/overview) to define the structure
and branding of the issued credential.
1. In the navigation panel on the left-hand side, expand the **Credential Issuance** menu.
2. Select **mDocs**.
3. Select the **Create new** button.
4. In the *Name* text box, enter a clear and descriptive title that will appear on the credential in
the wallet, for example "My First Credential".
5. In the *Description* text box, enter a clear and descriptive description that will appear on the
credential in the wallet, for example "Use For High Assurance Interactions".
6. In the *Credential type* text box, enter a unique identifier for the credential type, for example
`com.example.myfirstcredential`.
7. Copy and paste the following JSON into the *Claim mappings* text box:
```json title="Claim mappings"
{
"com.example.personaldetails.1": {
"name": {
"mapFrom": "claims.name",
"type": "string"
},
"email": {
"mapFrom": "claims.email",
"type": "string"
}
}
}
```
8. Use the *Include status* dropdown list to select **Enable**. This will enable support for changing the revocation status of the issued credentials.
9. Enter "1" in the *Months* text box in the *Validity for* panel to set the credential expiration
period.
10. Select the **Create** button to create the credential configuration.
Make the following request to create a simple
[mDoc credentials configuration](/docs/issuance/credential-configuration/api-reference/mdocs#create-an-mdocs-credential-configuration)
that includes the holder's name and e-mail:
```http title="Request"
POST /v2/credentials/mobile/configurations
```
```json title="Request body"
{
"type": "com.example.myfirstcredential",
"expiresIn": {
"months": 1
},
"claimMappings": {
"com.example.personaldetails.1": {
"name": {
"mapFrom": "claims.name",
"type": "string"
},
"email": {
"mapFrom": "claims.email",
"type": "string"
}
}
},
"branding": {
"name": "My First Credential",
"description": "For High Assurance Interactions",
"backgroundColor": "#2d46d8"
},
"includeStatus": true
}
```
The response will include an `id` element. You will use it to create a Credential offer in the next
step.
### Create a Credential offer [#create-a-credential-offer]
You now have all the pieces in place and can wrap them all together to generate a
[Credential offer](/docs/issuance/credential-offer/overview) and share it with the intended holder so
that they know what credentials are being offered and by whom.
1. In the navigation panel on the left-hand side, expand the **Credential Issuance** menu.
2. Select **Credential offer**.
3. Use the *Workflow* radio button to select **Authorization code flow**.
4. Select the **Select** button.
5. Check the checkbox next to the credential configuration you created in the previous step.
6. Select the **Apply** button.
7. Select the **Generate** button.
8. Download the displayed QR code and save it somewhere safe.\
This QR code will be used by the holder to claim the credential.
Make the following request to
[generate a new Credential offer](/docs/issuance/authorization-code/api-reference#create-credential-offer):
```http title="Request"
POST /v1/openid/offers
```
```json title="Request body"
{
"credentials": ["945214ad-3635-4aff-b51d-61d69a3c8eee"]
}
```
* `credentials`: Populate the array with the `id` element returned in the response
when you created an mDocs credentials configuration in the previous step.
The response will include a `uri` element which can be used by a digital wallet to trigger the
OID4VCI workflow. Use one of the following tools to convert the `uri` value to a QR code (make sure
you use the `Plain text` option where available):
* [https://www.the-qrcode-generator.com/](https://www.the-qrcode-generator.com/)
* [http://goqr.me/api/](http://goqr.me/api/)
* [https://www.qr-code-generator.com/](https://www.qr-code-generator.com/)
MATTR is not affiliated with any of these service providers and cannot vouch
for their offerings.
Save the generated QR code on your computer.
### Claim the credential [#claim-the-credential]
1. Open the GO hold example app.
2. Select **Scan**.
3. Scan the QR code generated in the previous step.
4. Review the credential offer and select **Accept**.
5. Follow the issuance workflow instructions to claim the credential.
Congratulations, you just configured an end-to-end
[OID4VCI Authorization Code flow](/docs/issuance/authorization-code/overview) to issue an mDoc
into a digital wallet!!!
## What's next? [#whats-next]
In this tutorial we have configured a basic
[OID4VCI Authorization Code flow](/docs/issuance/authorization-code/overview). However, you can
use MATTR VII to create more complex and rich issuance experience. Check out more resources on MATTR
Learn that will enable you to:
* Experiment with a different issuance flow by completing the
[OID4VCI Pre-authorized Code flow](/docs/issuance/pre-authorized-code/tutorial) tutorial.
* Configure a [Claims source](/docs/issuance/claims-source/tutorial) to retrieve data from
compatible data sources and use it in the issued credential.
* Configure an [Interaction hook](/docs/issuance/authorization-code/interaction-hook/tutorial) to redirect the user to
custom components as part of the issuance workflow.
* Apply branding to issued credentials as part of creating a
[Credential configuration](/docs/issuance/credential-configuration/overview).
# How to create issuer certificates
URL: /docs/issuance/certificates/guide
## Overview [#overview]
An [IACA](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca) (Issuing Authority
Certificate Authority) is a X.509 based certificate used to identify an mDoc issuer and verify the
mDocs they issue.
MATTR VII supports both managed and
unmanaged issuer certificates, allowing issuers to choose how they want to manage their certificate
infrastructure:
* **Managed IACAs** : MATTR VII automatically creates, stores, and manages the lifecycle of the IACA,
including the private key, as well as signing and managing any Document Signer Certificates (DSCs)
that are required to sign mDocs.
* **Unmanaged (external) IACAs** : The customer creates and manages their own IACA, including the private key,
and registers it with MATTR VII. They are then responsible for signing and managing any Document Signer
Certificates (DSCs) and Status List Signer Certificates (SLSCs) that are required to sign mDocs and Status lists.
## Creating an IACA [#creating-an-iaca]
1. Log into the [MATTR Portal](https://portal.mattr.global/).
2. In the navigation panel on the left-hand side, expand the **Platform Management** menu.
3. Select **Certificates**.
4. Select the **Create new** button.
5. Use the *Type* radio button to select **IACA - Issuing Authority Certificate Authority**.
6. Use the *Management method* radio button to select **MATTR managed**.
7. Use the *Country* dropdown list to select an issuing country.
8. Select the **Create** button to create the IACA certificate.\
The IACA is created as *inactive* by default.
9. Use the *Status* radio button to select **Active**.
10. Select the **Update** button to activate the IACA certificate.
**Create a managed IACA**
Make a request of the following structure to
[create a managed IACA](/docs/issuance/certificates/api-reference/iaca#create-an-iaca):
```http title:"Request"
POST /v2/credentials/mobile/iacas
```
```json title:"Request body"
{
"commonName": "Example IACA",
"country": "US",
"stateOrProvinceName": "US-AL",
"notBefore": "2024-09-26",
"notAfter": "2034-09-26"
}
```
* `commonName` : This *optional* parameter indicates the common name of the IACA certificate. When
specified, the value must be a valid `PrintableString` and cannot be an empty string. If not
provided and a [custom domain](/docs/platform-management/custom-domain-overview) is configured and verified, the
custom domain is used followed by the word *IACA*. If no custom domain is configured, the tenant
subdomain is used instead.
* `country` : This *optional* parameter indicates the issuer country. If not provided, a country is
selected based on the region of the tenant subdomain cloud host. When specified, the value must be
uppercase and a valid country code as per
[ISO 3166-1](https://www.iso.org/iso-3166-country-codes.html).
* `stateOrProvinceName`: This optional parameter indicates the issuer state or province. When
specified, the value must be uppercase and a valid country code as per
[ISO 3166-2](https://www.iso.org/standard/72483.html).
* `notBefore` : This *optional* parameter is used to set when the IACA becomes valid and can be used
to sign mDocs. This can be used alongside the `active` field to support
[IACA rotation](/docs/concepts/chain-of-trust#certificates-rotation) by creating inactive IACAs and distributing them
to relying parties in advance. When not provided, defaults to time of creation.
* `notAfter` : This *optional* parameter is used to set the date and time when the IACA expires.
When not provided, defaults to 10 years from `notBefore`, or from issuance if `notBefore` is not
provided. Maximum value is 20 years from issuance.
*Response*
```json filename:"Response body"
{
"id": "e86dd9bc-1414-4f60-aeb1-9143451424bb",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"certificateData": {
"commonName": "Example IACA",
"country": "US",
"stateOrProvinceName": "US-AL",
"notBefore": "2024-09-26T00:09:21.000Z",
"notAfter": "2034-09-26T00:09:21.000Z"
},
"certificateFingerprint": "57b178a6c2b8c1877dba515ad4fd60f9c805efc309287182db7debfe43a22928",
"publicKeyJwk": {
"kty": "EC",
"crv": "P-256",
"x": "kbuvX83YDOKHZMj7UXGXd3hlgsOAQe0rWTRkZriJa60",
"y": "pTK_1JY8eBRiNiZEHWDsRHOP-k5ICymTvVj5AD6P2c8"
},
"active": false,
"isManaged": true
}
```
* `id` : Unique identifier created for each IACA. You will use it to activate the IACA in the next step.
* `certificatePEM` : Certificate PEM format.
* `certificateData` : Key details regarding the created IACA:
* `commonName` : IACA's name, as provided in the request.
* `country` : IACA’s issuer country, as provided in the request.
* `stateOrProvinceName` : IACA’s issuer state/province, as provided in the request.
* `notBefore` : Data and time when IACA becomes valid.
* `notAfter` : Date and time when IACA expires.
* `certificateFingerprint` : Hashed value of the IACA certificate that includes all certificate data
and its signature.
* `publicKeyJwk` : JWK format of the IACA public key.
* `active`: IACA status. IACAs are always created as inactive, meaning they cannot be used to sign
mDocs until they are
[activated](/docs/issuance/certificates/api-reference/iaca#update-an-iaca). This is
useful for IACA rotation, allowing you to create a new IACA in advance and distribute it to
relying parties before it becomes active.
* `isManaged` : Indicates that this is a managed IACA.
**Activate the IACA**
Make a request of the following structure to
[update the unmanaged IACA](/docs/issuance/certificates/api-reference/iaca#update-an-iaca)
and activate it:
```http filename:"Request"
PUT /v2/credentials/mobile/iacas/{iacaId}
```
* `iacaId` : Replace with the `id` value obtained when you registered the unmanaged IACA.
```json filename:"Request structure"
{
"active": true
}
```
Once a managed IACA is activated, MATTR VII will automatically use it to sign DSCs required to sign
mDocs. Refer to [Certificate selection for signing mDocs](/docs/issuance/certificates/overview#certificate-selection)
for more information.
**Generate a self-signed root certificate (IACA)**
Use your preferred cryptographic library or tool to generate a self-signed root certificate (IACA).
This certificate will be used to sign the Document Signer Certificates (DSCs) for mDoc issuance.
Ensure the IACA meets the requirements specified in
[ISO/IEC 18013-5:2021 Annex B.1.2](https://www.iso.org/standard/69084.html).
When using unmanaged (external) certificates, the issuer assumes full responsibility for the secure
management of the uploaded root certificates and all subordinate certificates. This includes
ensuring the protection, proper issuance, and timely revocation of certificates under the uploaded
root, as MATTR VII does not manage or monitor these certificates on the issuer's behalf.
**Register the unmanaged IACA with MATTR VII**
1. Log into the [MATTR Portal](https://portal.mattr.global/).
2. In the navigation panel on the left-hand side, expand the **Platform Management** menu.
3. Select **Certificates**.
4. Select the **Create new** button.
5. Use the *Type* radio button to select **IACA - Issuing Authority Certificate Authority**.
6. Use the *Management method* radio button to select **Externally managed**.
7. Use the *Certificate PEM file* field to upload the PEM-encoded IACA certificate you
generated in the previous step.
8. Select the **Create** button to create the IACA certificate.\
The IACA is created as *inactive* by default, and new forms are displayed to enable you to create the required child certificates in the next steps.
Make a request of the following structure to
[create an unmanaged IACA](/docs/issuance/certificates/api-reference/iaca#create-an-iaca):
```http filename:"Request"
POST /v2/credentials/mobile/iacas
```
```json filename:"Request body"
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICDjCCAbSgAwIBAgIKdeZsA5NPKimuAzAKBggqhkjOPQQDAjAiMSAwCQYDVQQG\r\nEwJOWjATBgNVBAMTDEV4YW1wbGUgSUFDQTAeFw0yMzA5MTEyMzM0MjJaFw0zMzA5\r\nMDgyMzM0MjJaMCIxIDAJBgNVBAYTAk5aMBMGA1UEAxMMRXhhbXBsZSBJQUNBMFkw\r\nEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBbK7JKKFMWuu8kHQK2qaML+MQ0Ykk3Qg\r\n/p3TC6lQKvYJozPSpLXbJQIzMPq9u/dG+j4vq1iX/G/jFIwfiEiKEqOB0TCBzjAS\r\nBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIABjAdBgNVHQ4EFgQU9zTh\r\nKsqFxAgRJDDGW1au+ewJK6owHgYDVR0SBBcwFYYTaHR0cHM6Ly9leGFtcGxlLmNv\r\nbTBpBgNVHR8EYjBgMF6gXKBahlhodHRwczovL2V4YW1wbGUuY29tL3YyL2NyZWRl\r\nbnRpYWxzL21vYmlsZS9pYWNhcy8yZTg5YzE1Ni0zMWQ1LTQ3ODMtYmQ1OS05MDU1\r\nYjVmOGU3ZDIvY3JsMAoGCCqGSM49BAMCA0gAMEUCIQDD+eU8iOsYYC0v41L94fhF\r\nZ0brPo4gx2aRxrhE3NLFpwIgIgHCPBXJ+JICJg3K7dEsr153So4SEZzAA1rRn4eF\r\nvkM=\r\n-----END CERTIFICATE-----\r\n"
}
```
* `certificatePem` : This required parameter contains the PEM-encoded IACA certificate. The
certificate must meet the following requirements:
* Valid
* Not expired
* Compliant with [ISO/IEC 18013-5:2021 Annex B.1.2](https://www.iso.org/standard/69084.html)
The response will include an `id` property, which is a unique identifier for the unmanaged IACA.
This identifier will be used in subsequent operations to reference this unmanaged IACA.
**Create a Document Signer**
1. Scroll down to the *Child certificates* area.
2. In the *Document Signer Certificate* section, select the **Add new** button.
3. Select the **Create** button in the pop up window.\
A new Document Signer Certificate (DSC) will be created, and you'll be navigated to its details screen.
4. In the *Download the DSC Certificate Signing Request* section, download the DSC CSR file.\
This file contains the PEM-encoded Certificate Signing Request (CSR) that you'll use to generate
and sign the DSC in the next step.
Make a request of the following structure to
[create a Document Signer](/docs/issuance/certificates/api-reference/document-signers#create-a-document-signer)
that references the unmanaged IACA:
```http filename:"Request"
POST /v2/credentials/mobile/document-signers
```
```json filename:"Request body"
{
"iacaId": "080c670a-2e90-4023-b79f-b706e55e9bc6"
}
```
* `iacaId` : Replace with the `id` value obtained when you created the unmanaged IACA in the
previous step. Attempts to provide a managed IACA identifier for manual Document Signer creation
will result in an error.
The response will include two properties which you will use later in this guide:
* `id` : The unique identifier for the Document Signer. This identifier will be used in subsequent
operations to reference this Document Signer.
* `csrPem` : The X.509 Certificate Signing Request (CSR) in PEM format. You will use this CSR to
generate a valid Document Signer Certificate in the next step.
**Generate and sign the Document Signer Certificate (DSC)**
Use your preferred cryptographic library or tool to generate and sign a Document Signer Certificate
(DSC) using the CSR provided in the response from the previous step. Refer to the
[certificate requirements](/docs/issuance/certificates/overview#certificate-requirements)
section in the external issuer certificates documentation for details on how to structure a valid
DSC.
Because you control the validity period of unmanaged certificates, set the DSC to expire as soon
as is practical for your use case. Shorter-lived signer certificates limit the impact of a key
compromise. Balance this against the period your issued mDocs must remain verifiable, as an mDoc
can no longer be verified once its DSC expires. For guidance on choosing validity periods, see
[Certificates validity recommendations](/docs/issuance/certificates/overview#recommendations).
**Associate the DSC with the Document Signer**
1. Return to the Document Signer details screen in the MATTR Portal.
2. In the *Upload signed DSC* section, use the *Certificate PEM file* field to upload the PEM-encoded DSC you created in the previous step.
3. Use the *Status* radio button to select **Active**.
4. Select the **Update** button to associate the DSC with the Document Signer.
Make a request of the following structure to
[update the Document Signer](/docs/issuance/certificates/api-reference/document-signers#update-a-document-signer)
to activate and associate it with the generated DSC:
```http filename:"Request"
PUT /v2/credentials/mobile/document-signers/{documentSignerId}
```
* `documentSignerId` : Replace with the `id` value obtained when you created the Document Signer in
the previous step.
```json filename:"Request body"
{
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICbzCCAhSgAwIBAgIKfS7sskyJEh+DOzAKBggqhkjOPQQDAjAiMSAwCQYDVQQG\r\nEwJOWjATBgNVBAMTDEV4YW1wbGUgSUFDQTAeFw0yMzA5MTEyMzM0MjJaFw0yNDA5\r\nMTAyMzM0MjJaMDExLzAJBgNVBAYTAk5aMCIGA1UEAxMbZXhhbXBsZS5jb20gRG9j\r\ndW1lbnQgU2lnbmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7fa+jv9zCtHQ\r\nmKn7o1dS6lBHD5thlhPqjlx7qEfqy8Im9AcQJDal2sr/fUxhHwf/G4ublS7AL04U\r\n73dzr/ozxaOCASEwggEdMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFLdNNPTmPxt0\r\nLqvlZnV/QL86MXOxMB8GA1UdIwQYMBaAFPc04SrKhcQIESQwxltWrvnsCSuqMA4G\r\nA1UdDwEB/wQEAwIAgDAeBgNVHREEFzAVhhNodHRwczovL2V4YW1wbGUuY29tMB4G\r\nA1UdEgQXMBWGE2h0dHBzOi8vZXhhbXBsZS5jb20waQYDVR0fBGIwYDBeoFygWoZY\r\naHR0cHM6Ly9leGFtcGxlLmNvbS92Mi9jcmVkZW50aWFscy9tb2JpbGUvaWFjYXMv\r\nMmU4OWMxNTYtMzFkNS00NzgzLWJkNTktOTA1NWI1ZjhlN2QyL2NybDASBgNVHSUE\r\nCzAJBgcogYxdBQECMAoGCCqGSM49BAMCA0kAMEYCIQCfgn6+QoNfDVelJANl+Jp9\r\ncq7X9paZylfnI6UGr1FM6gIhAIzhiyclDa8+/ZSRfu7KfgGrNRaJ8YQ6vevskJls\r\nIavC\r\n-----END CERTIFICATE-----\r\n"
}
```
* `active` : This required boolean indicates whether the Document Signer is active or not. Can only
be set to `true` when a `certificatePem` is provided. Only active Document Signers can be used to
sign mDocs.
* `certificatePem` : This required parameter contains the PEM-encoded DSC created in the previous
step.
**Create a Status List Signer**
The following steps are only required when implementing mDocs revocation
capabilities. If you are not implementing revocation, you can skip to step 9.
1. Return to the IACA certificate detail screen in the MATTR Portal.
2. Scroll down to the *Child certificates* area.
3. In the *Status List Signer Certificate* section, select the **Add new** button.
4. Select the **Create** button in the pop up window.\
A new Status List Signer Certificate (SLSC) will be created, and you'll be navigated to its details screen.
5. In the *Download the SLSC Certificate Signing Request* section, download the SLSC CSR file.\
This file contains the PEM-encoded Certificate Signing Request (CSR) that you'll use to generate
and sign the SLSC in the next step.
Make a request of the following structure to
[create a Status List Signer](/docs/issuance/revocation/api-reference/mdocs-status-list-signers#create-a-status-list-signers)
that references the unmanaged IACA:
```http filename:"Request"
POST /v2/credentials/mobile/status-list-signers
```
```json filename:"Request body"
{
"iacaId": "080c670a-2e90-4023-b79f-b706e55e9bc6"
}
```
* `iacaId` : Replace with the `id` value obtained when you created the unmanaged IACA in the
previous step. Attempts to provide a managed IACA identifier for manual Status List Signer
creation will result in an error.
The response will include two properties which you will use later in this guide:
* `id` : The unique identifier for the Status List Signer. This identifier will be used in
subsequent operations to reference this Status List Signer.
* `csrPem` : The X.509 Certificate Signing Request (CSR) in PEM format. You will use this CSR to
generate a valid Document Signer Certificate in the next step.
**Generate and sign the Status List Signer Certificate (SLSC)**
Use your preferred cryptographic library or tool to generate and sign a Status List Signer
Certificate (SLSC) using the CSR provided in the response from the previous step. Refer to the
[certificate requirements](/docs/issuance/certificates/overview#certificate-requirements)
section in the external issuer certificates documentation for details on how to structure a valid
SLSC.
As with the DSC, set the SLSC to expire as soon as is practical for your use case. Shorter-lived
signer certificates limit the impact of a key compromise. For guidance on choosing validity
periods, see
[Certificates validity recommendations](/docs/issuance/certificates/overview#recommendations).
**Associate the SLSC with the Status List Signer**
1. Return to the Status List Signer details screen in the MATTR Portal.
2. In the *Upload signed SLSC* section, use the *Certificate PEM file* field to upload the PEM-encoded SLSC you created in the previous step.
3. Use the *Status* radio button to select **Active**.
4. Select the **Update** button to associate the SLSC with the Status List Signer.
Make a request of the following structure to
[update the Status List Signer](/docs/issuance/revocation/api-reference/mdocs-status-list-signers#update-a-status-list-signer)
to active and associate it with the Status List Signer:
```http filename:"Request"
PUT /v2/credentials/mobile/status-list-signers/{statusListSignerId}
```
* `statusListSignerId` : Replace with the `id` value obtained when you created the Status List
Signer in the previous step.
```json filename:"Request body"
{
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICbzCCAhSgAwIBAgIKfS7sskyJEh+DOzAKBggqhkjOPQQDAjAiMSAwCQYDVQQG\r\nEwJOWjATBgNVBAMTDEV4YW1wbGUgSUFDQTAeFw0yMzA5MTEyMzM0MjJaFw0yNDA5\r\nMTAyMzM0MjJaMDExLzAJBgNVBAYTAk5aMCIGA1UEAxMbZXhhbXBsZS5jb20gRG9j\r\ndW1lbnQgU2lnbmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7fa+jv9zCtHQ\r\nmKn7o1dS6lBHD5thlhPqjlx7qEfqy8Im9AcQJDal2sr/fUxhHwf/G4ublS7AL04U\r\n73dzr/ozxaOCASEwggEdMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFLdNNPTmPxt0\r\nLqvlZnV/QL86MXOxMB8GA1UdIwQYMBaAFPc04SrKhcQIESQwxltWrvnsCSuqMA4G\r\nA1UdDwEB/wQEAwIAgDAeBgNVHREEFzAVhhNodHRwczovL2V4YW1wbGUuY29tMB4G\r\nA1UdEgQXMBWGE2h0dHBzOi8vZXhhbXBsZS5jb20waQYDVR0fBGIwYDBeoFygWoZY\r\naHR0cHM6Ly9leGFtcGxlLmNvbS92Mi9jcmVkZW50aWFscy9tb2JpbGUvaWFjYXMv\r\nMmU4OWMxNTYtMzFkNS00NzgzLWJkNTktOTA1NWI1ZjhlN2QyL2NybDASBgNVHSUE\r\nCzAJBgcogYxdBQECMAoGCCqGSM49BAMCA0kAMEYCIQCfgn6+QoNfDVelJANl+Jp9\r\ncq7X9paZylfnI6UGr1FM6gIhAIzhiyclDa8+/ZSRfu7KfgGrNRaJ8YQ6vevskJls\r\nIavC\r\n-----END CERTIFICATE-----\r\n"
}
```
* `active` : This required boolean indicates whether the Status List Signer is active or not. Can
only be set to `true` when a `certificatePem` is provided. Only active Status List Signers can be
used to sign mDocs.
* `certificatePem` : This required parameter contains the PEM-encoded Status List Signer Certificate
(SLSC) created in the previous step.
**Activate the IACA**
Once you have created and associated the required child certificates (DSC and, if applicable, SLSC), you can activate the unmanaged IACA to make it available for signing mDocs.
1. Return to the IACA certificate detail screen in the MATTR Portal.
2. Use the *Status* radio button to select **Active**.
3. Select the **Update** button to activate the IACA certificate.
Make a request of the following structure to
[update the unmanaged IACA](/docs/issuance/certificates/api-reference/iaca#update-an-iaca)
and activate it:
```http filename:"Request"
PUT /v2/credentials/mobile/iacas/{iacaId}
```
* `iacaId` : Replace with the `id` value obtained when you registered the unmanaged IACA.
```json filename:"Request body"
{
"active": true
}
```
## Usage [#usage]
**Signing the mDoc**
Once the IACA is activated, it can be used to sign mDocs. MATTR VII will automatically select a valid Document Signer based on the [certificate selection logic](/docs/issuance/certificates/overview#certificate-selection).
If there is no Document Signer that meets the selection criteria, MATTR VII will automatically create a suitable one. For example, if you attempt to sign an mDoc with an expiry date later than the `notAfter` date of all available Document Signers, MATTR VII will create a new Document Signer that can accommodate the requested expiry date.
**Signing the Status List Token**
If the issued mDoc is configured as revocable, it will be associated with a
[Status List](/docs/issuance/revocation/overview#status-list). MATTR VII will then automatically attempt to sign
a Status List Token for that Status List using an appropriate Status List Signer. The Status List
Signer is selected according to the following criteria:
* The Status List Signer must be active.
* The Status List Signer must reference the same IACA as the Document Signer used to sign the mDoc.
If there is no Status List Signer that meets the selection criteria, MATTR VII will automatically create a suitable one. For example, if you attempt to sign an mDoc using a Document Signer that was created with an IACA that does not have an active Status List Signer, MATTR VII will create a new Status List Signer that meets the criteria.
**Signing the mDoc**
Once the Document Signer and IACA are activated, they can be used to sign mDocs. MATTR VII will
automatically select a valid Document Signer based on the
[certificate selection logic](/docs/issuance/certificates/overview#certificate-selection).
If there is no Document Signer that meets the selection criteria, MATTR VII will return an error
stating that no valid Document Signer is available for signing. For example, if you attempt to sign
an mDoc with an expiry date later than the `notAfter` date of all available Document Signers, MATTR
VII will not find a suitable Document Signer and will return an error.
Unlike the managed IACA flow, MATTR VII does not automatically create new Document Signers in the
unmanaged flow, and the issuer is responsible for manually creating and uploading them as needed.
**Signing the Status List Token**
If the issued mDoc is configured as revocable, it will be associated with a
[Status List](/docs/issuance/revocation/overview#status-list). MATTR VII will then automatically attempt to sign
a Status List Token for that Status List using an appropriate Status List Signer. The Status List
Signer is selected according to the following criteria:
* The Status List Signer must be active.
* The Status List Signer must reference the same IACA as the Document Signer used to sign the mDoc.
If there is no Status List Signer that meets the selection criteria, MATTR VII will return an error
stating that no valid Status List Signer is available for signing. For example, if you attempt to
sign an mDoc using a Document Signer that was created with an IACA that does not have an active
Status List Signer, MATTR VII will not find a suitable Status List Signer and will return an error.
Unlike the managed IACA flow, MATTR VII does not automatically create new Status List Signers in the
unmanaged flow, and the issuer is responsible for manually creating and uploading them as needed.
## IACA Distribution [#iaca-distribution]
IACAs can be distributed via different mechanisms, for example using a
[Verified Issuer Certificate Authority List (VICAL)](/docs/digital-trust-service/vical-overview).
Alternatively, all active IACAs on a given tenant can be retrieved by:
1. Making a [GET request](/docs/issuance/certificates/api-reference/issuer-metadata) to the issuer's `/.well-known/openid-credential-issuer` endpoint.
2. Inspecting the `mdoc_iacas_uri` property in the response and obtaining the IACA distribution URL. It will be structured as follows:
`https://{tenant-subdomain}/core/v1/openid/iacas`.
3. Making a GET request to the IACA distribution URL. This would return a list of all active IACAs for that tenant.
# Overview
URL: /docs/issuance/certificates/overview
## Chain of trust [#chain-of-trust]
When issuers issue mDocs they must ensure that relying parties can
verify the authenticity and integrity of these credentials. This is accomplished using a [chain of trust](/docs/concepts/chain-of-trust),
a hierarchy of certificates that proves the issuer’s authenticity.
The following diagram depicts how MATTR implements the chain of trust model when signing mDocs:
### Issuing Authority Certificate Authority (IACA) [#issuing-authority-certificate-authority-iaca]
The root certificate, operated by or on behalf of an issuing authority. The IACA is a X.509
certificate used to identify an mDoc issuer and verify the mDocs they issue. An IACA can be valid
for up to 20 years, and is used to sign Document Signer Certificates (DSCs), which are then in turn
used to sign Mobile Security Objects (MSO) in mDocs. Refer to
[Certificate selection for signing mDocs](#certificate-selection-for-signing-mdocs) below to learn
more about how MATTR VII automatically selects a suitable IACA when signing mDocs.
Each IACA includes the following elements:
* **Signature**: As the IACA is the root certificate, it is self-signed and verified against its own
public key also in the certificate.
* **Public key**: Used to verify the IACA’s certificate signature, as well as the DSC signature. The
private key linked to the certificate is stored and managed in the highly secure and reliable Key
Management System (KMS).
* **Validity information:** When the certificate becomes valid or expires, as well as information
about how to check its revocation status.
IACAs can be distributed via different mechanisms, for example using a
[Verified Issuer Certificate Authority List (VICAL)](#verified-issuer-certificate-authority-list-vical).
Alternatively, all active IACAs on a given tenant can be retrieved by:
1. Making a [GET request](/docs/issuance/certificates/api-reference/issuer-metadata) to the issuer's `/.well-known/openid-credential-issuer` endpoint.
2. Inspecting the `mdoc_iacas_uri` property in the response and obtaining the IACA distribution URL. It will be structured as follows:
`https://{tenant-subdomain}/core/v1/openid/iacas`.
3. Making a GET request to the IACA distribution URL. This would return a list of all active IACAs for that tenant.
### Document Signer Certificate (DSC) [#document-signer-certificate-dsc]
The end-entity certificate. The DSC is a X.509 certificate used to digitally sign Mobile Security
Objects (MSOs) in mDocs. The DSC itself must be issued and signed by the root certificate, which is
the Issuing Authority Certificate Authority (IACA).
To understand how mDocs are being signed, it is important to differentiate between a Document Signer
and the Document Signer Certificate (DSC). The Document Signer is a logical entity that represents
the device or application that signs the mDoc. It is responsible for signing the MSO payload
contained within the mDoc. The DSC, on the other hand, is the actual digital certificate that
authenticates the Document Signer and is used to verify the signature of the MSO
To sign an mDoc, you must use an active Document Signer that is linked to a valid DSC. The DSC is
embedded within the [signed mDoc](/docs/concepts/mdocs/structure-to-function), so verifiers only need to
obtain the IACA to validate the mDoc. For details on how MATTR VII automatically manages DSC
generation and selection during mDoc signing, see
[Certificate selection for signing mDocs](#certificate-selection-for-signing-mdocs) below.
Each DSC includes the following elements:
* **Signature**: DSCs are signed by the IACA, and this signature is verified against the IACA’s
public key.
* **Public key**: Used to verify the MSO signature.
* **Validity information:** When the certificate becomes valid or expires, as well as information
about how to check its revocation status.
The ISO/IEC 18013:5:2021 standard determines that a chain of certificates that is used to sign an
mDL can only include one IACA and one end-entity certificate, which is the DSC. Other mDocs do not
have that limitation and can have more than one intermediate certificate, linking the IACA to the
end-entity certificate.
### Mobile Security Object (MSO) [#mobile-security-object-mso]
The end-entity of the chain of trust, which is the issued signed data. mDocs include an `IssuerAuth`
data structure, as defined in the
[ISO/IEC 18013-5:2021 standard](https://www.iso.org/standard/69084.html). It contains the MSO
payload, which comprises metadata and salted hashed claims.
Each MSO includes the following elements:
* **Signature**: MSOs are signed by the DSC. Their signature is checked against the DSC public key,
and the DSC is validated all the way up to the IACA.
* **Public key**: Used to verify the signature of the device the mDoc was issued to, enabling device
authentication.
* **Validity information:** When the MSO becomes valid or expires, as well as information about how
to check its revocation status.
## Supported cryptographic algorithms and curves [#supported-cryptographic-algorithms-and-curves]
MATTR VII supports different cryptographic algorithms and curves for IACAs and DSCs, depending on whether the issuer is using MATTR VII managed certificates or
[unmanaged (external) certificates](/docs/concepts/chain-of-trust#external-certificates).
### Managed IACAs and DSCs [#managed-iacas-and-dscs]
Managed IACAs and DSCs **only** support **ECDSA with `P-256` and SHA-256** as the signature algorithm. Within X.509 certificates, this is represented by the OID `1.2.840.10045.4.3.2` (`ecdsa-with-SHA256`).
### Unmanaged IACAs [#unmanaged-iacas]
Unmanaged IACAs support the following elliptic curve signature algorithms and curves:
#### ECDSA [#ecdsa]
The **Elliptic Curve Digital Signature Algorithm (ECDSA)** is supported with the following curves:
* `P-256`
* `P-384`
* `P-521`
* `brainpoolP256r1`
* `brainpoolP320r1`
* `brainpoolP384r1`
* `brainpoolP512r1`
#### EdDSA [#eddsa]
The **Edwards-curve Digital Signature Algorithm (EdDSA)** is supported with the following curves:
* `Ed25519`
* `Ed448`
### Compatibility considerations [#compatibility-considerations]
When using managed certificates, ECDSA with `P-256` curve is the only supported option for both IACAs and DSCs. This choice is to ensure maximum compatibility across all verifiers and holders, given the P-256 curve is the most widely supported. This includes MATTR's own Pi Verifier and Holder SDKs, as well as third-party implementations.
When using unmanaged certificates (for example when [creating an unmanaged IACA](/docs/issuance/certificates/guide#unmanaged-1)), you must ensure the selected algorithm and curve are supported by the verifiers and holders that will interact with the mDocs you issue. If you select a curve that is not widely supported, some holders may not be able to store the mDoc in their digital wallet, and some verifiers may not be able to verify it.
If your implementation is using MATTR Pi Holder and/or Verifier SDKs, you can refer to the mDocs [Holder](/docs/holding/sdk-overview#isoiec-18013-5) and [Verifier](/docs/verification/sdks/overview#supported-isoiec-18013-5-features) SDK documentation for details on the algorithms and curves they support.
## Certificates validity [#certificates-validity]
The concept of certificates validity is of paramount importance in the context of mDocs. For an mDoc
to be verified, all the certificates within its chain of trust must be valid. If during the
verifications process any of the certificates have expired, the mDoc cannot be verified:
Careful analysis must be conducted before determining the certificate lifecycle. IACAs are usually
created with long expiry dates, some as long as 10-15 years, while DSCs and SLSCs tend to have
shorter validity periods. There are a variety of trade-offs in making this duration longer or
shorter.
Shorter validity periods improve security. They limit the window in which a compromised private key
can be misused, and they reduce the number of credentials affected if a key is exposed. Shorter
periods also enable faster key rotation intervals and provide the opportunity to change the
cryptographic algorithms being used if required.
On the other hand, short validity periods come with increased deployment overhead. Whenever a new
IACA is issued, the certificate must be distributed to all verifiers that verify mDocs from that
issuer.
Understanding the unique needs and requirements of ecosystems is crucial to applying correct
certificates validity periods.
### Recommendations [#recommendations]
As a general principle, set each certificate's validity period to the shortest duration that remains
practical for your ecosystem, balancing the security benefits of shorter-lived certificates against
the operational overhead of rotating and redistributing them. This is particularly important for
signer certificates (DSCs and SLSCs). These end-entity certificates directly sign issued data (DSCs
sign mDocs, and SLSCs sign status lists), so a compromise of their private keys carries the most
immediate impact.
This guidance is most relevant when using
[unmanaged (external) certificates](/docs/concepts/chain-of-trust#external-certificates), where you
control the validity periods of your IACAs, DSCs, and SLSCs directly. When using managed
certificates, MATTR VII already issues short-lived DSCs automatically and rotates them on your
behalf, in line with the [certificate selection logic](#certificate-selection).
When choosing a validity period, consider the following:
* **Signer certificates (DSCs and SLSCs)**: Favor shorter validity periods. Frequent rotation limits
the exposure of a compromised signer key. Rotation overhead is comparatively low for DSCs, as a DSC
is embedded in the mDocs it signs and does not need to be distributed to verifiers separately.
* **IACAs**: Balance the security benefits of a shorter validity period against the overhead of
distributing each new IACA to all relying parties that verify your mDocs. Plan rotation in advance
so that a replacement IACA can be distributed before the current one expires. See
[Certificates rotation](/docs/concepts/chain-of-trust#certificates-rotation) for more detail.
## Certificate selection [#certificate-selection]
When signing an mDoc and issuing it to a holder, MATTR VII automatically selects IACA and DSC
certificates to ensure a valid mDoc is issued. The selection criteria is consistent whether the
issuer is using MATTR VII managed or
[unmanaged (external) certificates](/docs/concepts/chain-of-trust#external-certificates).
### IACA selection [#iaca-selection]
MATTR VII will first attempt to select an IACA that is:
* Active.
* Valid for the required time period:
* Credential cannot be valid before the IACA becomes valid.
* Credential cannot be valid after the IACA expires.
If no IACAs meet these requirements, issuance will fail.
### DSC selection [#dsc-selection]
After selecting an IACA, MATTR VII examines all existing Document Signers with DSCs signed by that
IACA. It then tries to select a Document Signer that is:
* Active.
* Its DSC is valid for the required time period:
* Credential cannot be valid before the DSC becomes valid.
* Credential cannot be valid after the DSC expires.
If multiple DSCs meet these requirements, the most recently created one will be used.
If no existing DSCs meet these requirements and the issuer is using
[unmanaged (external) issuer certificates](/docs/concepts/chain-of-trust#external-certificates), issuance will
fail.
If no existing DSCs meet these requirements and the issuer is using managed certificates, MATTR VII
will automatically generate a new DSC that meets the following criteria:
* Valid from time of creation.
* Valid until the later of the following:
* 30 days from the DSC creation date.
* 30 days from the signed mDoc expiry date.
* Regardless of these validity settings:
* The DSC cannot be valid after the IACA used to sign it expires.
* The DSC cannot be valid for more than 10 years (3650 days).
* The DSC cannot be valid for more than 457 days if the signed mDoc is an mDL (as
indicated by the
[credential configuration](/docs/issuance/credential-configuration/api-reference/mdocs#create-an-mdocs-credential-configuration!path=type\&t=request)
`type`).
* Suitable for signing the requested mDoc type (DSCs generated by MATTR VII can sign any type of mDoc).
DSCs generated by MATTR can be used to sign any mDocs format. If your
verification system enforces strict Extended Key Usage (EKU) validation,
ensure it supports multiple key usages as appropriate.
## Certificate revocation [#certificate-revocation]
Managed DSCs may be [revoked](/docs/issuance/certificates/api-reference/document-signers#revoke-a-document-signer) for reasons such as key compromise, role change, or decommissioning. Once revoked, the DSC’s serial number is published in the Certificate Revocation List (CRL) referenced by its issuing IACA.
Verifiers that retrieve and process the CRL referenced in the IACA must treat a revoked DSC, and any mDocs it signed, as invalid.
Revocation through MATTR VII is only available when the IACA is managed by MATTR VII, as MATTR VII holds the IACA private key and signs the CRL on your behalf. For unmanaged (external) IACAs, you must revoke the certificate directly with the CA that issued it. For step-by-step instructions, see [How to revoke signing certificates](/docs/issuance/certificates/revoke-signing-certificates).
## Certificate requirements [#certificate-requirements]
The following lists depicts the requirements for external certificates used in MATTR VII. Some of
the requirements are common across all certificates, while others are specific to the type of
certificate (IACA, DSC, SLSC).
### Common certificate requirements [#common-certificate-requirements]
* Certificate format & basic attributes:
* PEM format must contain a valid X.509 certificate.
* Version must be v3.
* `Issuer` field must be present and valid.
* Issuer Alternative Name must be present and contain a valid email address or URI.
* Serial Number:
* Must be present.
* Must contain 1-20 digits (**Best practice**: Use a positive, non-sequential value).
* Subject attributes:
* Subject field must be present:
* Country (C): must be present and be a valid
[ISO 3166-1 alpha-2 code](https://www.iso.org/glossary-for-iso-3166.html).
* Common Name (CN): must be present.
* If State or Province (ST) is present, it must be a valid
[ISO 3166-2 code](https://www.iso.org/glossary-for-iso-3166.html) and match the Country
(C).
* Public Key requirements:
* Subject Public Key must be present.
* Extensions:
* Certificate must include extensions.
* Duplicate extensions are not allowed (No more than one extension with the same `extnID`).
* Mandatory extensions:
* Subject Key Identifier: Must be present and non-empty.
* Key Usage:
* Must be present.
* Must match the intended use of the certificate (e.g. IACA, DSC, SLSC).
* Validity period:
* `NotAfter` must be after `NotBefore`.
* `NotAfter` cannot be in the past (expired certificates are invalid).
* Future dated certificates are valid (e.g. `notBefore` can be in the future).
* Must be within allowed limits for certificate type:
* IACA: Maximum 20 years from issuance.
* DSC/SLSC: Maximum 10 years from issuance.
* Certificate Revocation List (CRL):
* If a CRL is provided, it must be valid and signed by the IACA.
* The CRL must be accessible via a valid URI.
### IACA specific requirements [#iaca-specific-requirements]
* Must include the `keyCertSign` and `cRLSign` key usages.
* Basic constraints must be present and `CA` must be set to `TRUE`.
* Issuer Alternative Name must be present and contain a valid email address or URI.
* Status List Distribution URI must be valid, if present:
* OID: `1.3.6.1.4.1.61546.100`
* Value: Must point to a valid location where the Status List can be retrieved (e.g.
`https://{tenant-subdomain}/v2/credentials/mobile/status-lists/distribution`).
* Signature must be self-signed and verifiable.
* Public key must use one of the supported public key algorithms and curves as defined in
ISO/IEC 18013-5:2021 B.3:
* ECDSA curves: `P-256`, `P-384`, `P-521`, `brainpoolP256r1`, `brainpoolP320r1`,
`brainpoolP384r1`, `brainpoolP512r1`
* EdDSA key types: `Ed25519`, `Ed448`
### DSC/SLSC specific requirements [#dscslsc-specific-requirements]
* Must be signed by a valid IACA.
* Common name must differ from parent/root IACA.
* `Issuer` field must be present and must match the exact binary value of the IACA certificate
subject.
* Must include the `digitalSignature` key usage exclusively.
* Extended key usage must be present and include the correct OID:
* For DSCs:
* `1.0.18013.5.1.2` (required, used for signing mDLs).
* `1.3.6.1.4.1.61546.0` (optional, used for signing any other mDocs).
* For SLSCs: `1.3.6.1.4.1.61546.1`.
* Signature must be verifiable against the IACA.
* Authority Key Identifier must be present and match the IACA's Subject Key Identifier.
* Must not exceed parent IACA's validity period (i.e. `notBefore` and `notAfter` must be within the
IACA's validity period).
* Public key must match the CSR provided during Document/Status List Signer creation.
### Example certificates [#example-certificates]
See an example of valid certificates parsed using the MATTR Labs
[X.509 certificate decoder](https://tools.mattrlabs.com/pem):
* [IACA](https://tools.mattrlabs.com/pem?cert=MIICDjCCAbSgAwIBAgIKdeZsA5NPKimuAzAKBggqhkjOPQQDAjAiMSAwCQYDVQQGEwJOWjATBgNVBAMTDEV4YW1wbGUgSUFDQTAeFw0yMzA5MTEyMzM0MjJaFw0zMzA5MDgyMzM0MjJaMCIxIDAJBgNVBAYTAk5aMBMGA1UEAxMMRXhhbXBsZSBJQUNBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBbK7JKKFMWuu8kHQK2qaML%252BMQ0Ykk3Qg%252Fp3TC6lQKvYJozPSpLXbJQIzMPq9u%252FdG%252Bj4vq1iX%252FG%252FjFIwfiEiKEqOB0TCBzjASBgNVHRMBAf8ECDAGAQH%252FAgEAMA4GA1UdDwEB%252FwQEAwIABjAdBgNVHQ4EFgQU9zThKsqFxAgRJDDGW1au%252BewJK6owHgYDVR0SBBcwFYYTaHR0cHM6Ly9leGFtcGxlLmNvbTBpBgNVHR8EYjBgMF6gXKBahlhodHRwczovL2V4YW1wbGUuY29tL3YyL2NyZWRlbnRpYWxzL21vYmlsZS9pYWNhcy8yZTg5YzE1Ni0zMWQ1LTQ3ODMtYmQ1OS05MDU1YjVmOGU3ZDIvY3JsMAoGCCqGSM49BAMCA0gAMEUCIQDD%252BeU8iOsYYC0v41L94fhFZ0brPo4gx2aRxrhE3NLFpwIgIgHCPBXJ%252BJICJg3K7dEsr153So4SEZzAA1rRn4eFvkM%253D)
* [DSC](https://tools.mattrlabs.com/pem?cert=MIICbzCCAhSgAwIBAgIKfS7sskyJEh%252BDOzAKBggqhkjOPQQDAjAiMSAwCQYDVQQGEwJOWjATBgNVBAMTDEV4YW1wbGUgSUFDQTAeFw0yMzA5MTEyMzM0MjJaFw0yNDA5MTAyMzM0MjJaMDExLzAJBgNVBAYTAk5aMCIGA1UEAxMbZXhhbXBsZS5jb20gRG9jdW1lbnQgU2lnbmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7fa%252Bjv9zCtHQmKn7o1dS6lBHD5thlhPqjlx7qEfqy8Im9AcQJDal2sr%252FfUxhHwf%252FG4ublS7AL04U73dzr%252FozxaOCASEwggEdMAwGA1UdEwEB%252FwQCMAAwHQYDVR0OBBYEFLdNNPTmPxt0LqvlZnV%252FQL86MXOxMB8GA1UdIwQYMBaAFPc04SrKhcQIESQwxltWrvnsCSuqMA4GA1UdDwEB%252FwQEAwIAgDAeBgNVHREEFzAVhhNodHRwczovL2V4YW1wbGUuY29tMB4GA1UdEgQXMBWGE2h0dHBzOi8vZXhhbXBsZS5jb20waQYDVR0fBGIwYDBeoFygWoZYaHR0cHM6Ly9leGFtcGxlLmNvbS92Mi9jcmVkZW50aWFscy9tb2JpbGUvaWFjYXMvMmU4OWMxNTYtMzFkNS00NzgzLWJkNTktOTA1NWI1ZjhlN2QyL2NybDASBgNVHSUECzAJBgcogYxdBQECMAoGCCqGSM49BAMCA0kAMEYCIQCfgn6%252BQoNfDVelJANl%252BJp9cq7X9paZylfnI6UGr1FM6gIhAIzhiyclDa8%252B%252FZSRfu7KfgGrNRaJ8YQ6vevskJlsIavC)
* [SLSC](https://tools.mattrlabs.com/pem?cert=MIIB%252BDCCAZ6gAwIBAgIUbDN4Ed27snUvA3ehITvqiib6iyAwCgYIKoZIzj0EAwIwJDEVMBMGA1UEAwwMRXhhbXBsZSBJQUNBMQswCQYDVQQGEwJOWjAeFw0yNTA2MjYyMjU4NDBaFw0yNjA2MjYyMjU4NDBaMDMxCzAJBgNVBAYTAk5aMSQwIgYDVQQDDBtFeGFtcGxlIFN0YXR1cyBMaXN0IFNpZ25lciAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARm5gwI2YzENXu4a4OS%252Bj3ym%252FhomivivOo1UDMNcdxGbjT5mrOgUv7B6hppPeHWLDl4gH9BspMkAtGptz8%252FTr%252FSo4GeMIGbMB8GA1UdIwQYMBaAFPfKMlWR%252ByWXdks8axVp5E2dKnUbMDAGA1UdEgQpMCeBJXRlc3RpYWNhQGFsdGVybmF0aXZlbmFtZS5tYXR0ci5nbG9iYWwwDgYDVR0PAQH%252FBAQDAgeAMBcGA1UdJQEB%252FwQNMAsGCSsGAQQBg%252BBqATAdBgNVHQ4EFgQUyXRX0QAvlnl7rKZ6OKZq%252F3o6WkAwCgYIKoZIzj0EAwIDSAAwRQIgWwoF5tG1mlMQQXc9jiOYW%252Fcfgf%252Bv9H70J1OwDyebY%252FECIQCBbqbrqbX2UhkvHSLRAvvAjnSodkCnljadOQTTj0f6TQ%253D%253D)
# How to revoke signing certificates
URL: /docs/issuance/certificates/revoke-signing-certificates
## Overview [#overview]
This guide explains how to revoke the signing certificates that sit below your
[IACA](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca) in the
[chain of trust](/docs/concepts/chain-of-trust):
* **[Document Signer Certificates (DSCs)](/docs/issuance/certificates/overview#document-signer-certificate-dsc)**,
which sign the Mobile Security Objects (MSOs) in your mDocs.
* **Status List Signer Certificates (SLSCs)**, which sign the
[Status Lists](/docs/issuance/revocation/overview#status-list) used for mDoc revocation.
When a signer can no longer be trusted, you revoke it so relying parties stop trusting the mDocs or
Status Lists it signed. Revoking a signer publishes the certificate's serial number to the
Certificate Revocation List (CRL) referenced by its issuing IACA. Verifiers that retrieve and
process that CRL should treat the revoked signer, and anything it signed, as untrusted. Revocation
does not delete already-issued mDocs, it withdraws trust in them at the certificate level.
For background on how the IACA, DSC, and Mobile Security Object (MSO) fit together, see
[Chain of trust](/docs/concepts/chain-of-trust) and the
[Certificates overview](/docs/issuance/certificates/overview#chain-of-trust).
## When to revoke a signer [#when-to-revoke-a-signer]
Revocation is the right tool when a signing certificate must be distrusted ahead of its natural
expiry. Common scenarios include:
* **Key compromise**: the private key associated with the signer is suspected or confirmed to be
exposed. Anything signed by that key can no longer be trusted, so the signer is revoked
immediately.
* **Role or authorization change**: the signer was operated by a team, environment, or partner that
is no longer authorized to issue on your behalf.
* **Misissuance**: the signer was used to sign mDocs in error, or was misconfigured, and you want to
invalidate its output.
You do not need to revoke a signer that is simply being rotated or is expiring on schedule. For
managed IACAs, MATTR VII rotates Document Signers for you and selects a valid signer at signing time,
so retiring an old signer does not require revocation. Revocation is for distrusting a signer before
its validity period ends.
## Prerequisites [#prerequisites]
* **Managed IACA**: the Document Signer or Status List Signer you want to revoke must have been
signed by a [managed IACA](/docs/issuance/certificates/guide#managed-1). When using
[unmanaged (external) IACAs](/docs/concepts/chain-of-trust#external-certificates), MATTR VII does
not hold the IACA private key and cannot sign a CRL for you. In that case you must revoke the
signer certificate directly with the CA that issued it, following your own PKI processes.
* **Permissions**: the API client needs the `admin` or `issuer` role.
* **The signer identifier**: the `id` of the Document Signer or Status List Signer you want to
revoke. You can obtain it from the response when the signer was created, or by
[listing your Document Signers](/docs/issuance/certificates/api-reference/document-signers#retrieve-all-document-signers)
or
[listing your Status List Signers](/docs/issuance/revocation/api-reference/mdocs-status-list-signers#retrieve-all-status-list-signers).
## How revocation and the CRL work [#how-revocation-and-the-crl-work]
When MATTR VII creates a managed IACA, it embeds a CRL Distribution Point (CDP) in the IACA
certificate. The CDP points to a public, MATTR VII hosted distribution endpoint for that IACA. You
can see this in the example certificates parsed with the MATTR Labs
[X.509 certificate decoder](https://tools.mattrlabs.com/pem) linked from the
[Certificates overview](/docs/issuance/certificates/overview#example-certificates).
When you revoke a signer:
1. MATTR VII adds the signer certificate's serial number to the CRL for the issuing IACA.
2. MATTR VII signs the CRL with the IACA key and serves it from the distribution endpoint embedded
in the IACA certificate.
3. Verifiers that follow the CDP retrieve the IACA-signed CRL and check the serial numbers of the
certificates in the mDoc chain against it.
The CRL distribution endpoint is public and does not require authentication, because relying parties
must be able to reach it to validate certificates. You can retrieve the current CRL for an IACA in
DER binary format:
```http title:"Request"
GET /v2/credentials/mobile/iacas/{iacaId}/crl
```
See
[Retrieve IACA CRL](/docs/api-reference/platform/iaca/get-mobile-credential-iaca-crl)
for the full endpoint reference.
## Revoke a Document Signer [#revoke-a-document-signer]
Make a request of the following structure to
[revoke a Document Signer](/docs/issuance/certificates/api-reference/document-signers#revoke-a-document-signer):
```http title:"Request"
POST /v2/credentials/mobile/document-signers/{documentSignerId}/revoke
```
* `documentSignerId`: Replace with the `id` of the Document Signer you want to revoke.
The request body is empty:
```json title:"Request body"
{}
```
```json title:"Response body"
{
"revoked": true,
"revocationDate": "2025-10-31T23:59:59Z"
}
```
* `revoked`: Indicates that the Document Signer has been revoked.
* `revocationDate`: ISO 8601 timestamp (UTC) indicating when the Document Signer was revoked.
Revocation is **permanent**. A signer cannot be un-revoked, and attempting to revoke a signer that is
already revoked returns a `409` response. Before revoking a signer that is actively in use, make
sure a valid replacement signer is available so issuance is not interrupted. For managed IACAs,
MATTR VII automatically generates a suitable Document Signer at signing time when none of the
existing signers meet the
[selection criteria](/docs/issuance/certificates/overview#dsc-selection).
### Example: revoking a compromised signer [#example-revoking-a-compromised-signer]
Suppose you operate a managed IACA and discover that the host running one of your signing
integrations may have leaked a Document Signer private key. You retrieve the affected signer's `id`,
then revoke it:
```bash title:"Example request"
curl -X POST \
"https://{tenant-subdomain}.vii.mattr.global/v2/credentials/mobile/document-signers/d2c9f2aa-fc69-4fbc-9b85-0c00591d72f6/revoke" \
-H "Authorization: Bearer {access-token}" \
-H "Content-Type: application/json" \
-d '{}'
```
Once revoked, the DSC serial number is published to the IACA CRL. New issuance will no longer select
the revoked signer (see
[Certificate selection](/docs/issuance/certificates/overview#certificate-selection)), and verifiers
that process the CRL should treat the mDocs it signed as untrusted.
## Revoke a Status List Signer [#revoke-a-status-list-signer]
The same model applies to Status List Signer Certificates (SLSCs), which sign the
[Status Lists](/docs/issuance/revocation/overview#status-list) used for mDoc revocation. If a Status
List Signer is compromised or must otherwise be distrusted, you can revoke it the same way, provided
it was issued under a managed IACA:
```http title:"Request"
POST /v2/credentials/mobile/status-list-signers/{statusListSignerId}/revoke
```
As with a Document Signer, send an empty JSON object (`{}`) as the request body. The response mirrors
the Document Signer response with a `revoked` flag and a `revocationDate`. Revocation is permanent,
and revoking a Status List Signer that is already revoked returns a `409` response. Verifiers that
process the CRL should treat the revoked Status List Signer, and any Status List it signed, as
untrusted. See
[Revoke a status list signer](/docs/issuance/revocation/api-reference/mdocs-status-list-signers#revoke-a-status-list-signer)
for the full endpoint reference.
## Revoking under unmanaged (external) IACAs [#revoking-under-unmanaged-external-iacas]
When your IACA is unmanaged, MATTR VII integrates the certificates you provide but does not hold the
IACA private key, so it cannot sign or publish a CRL on your behalf. To distrust a signer under an
unmanaged IACA, revoke the certificate directly with the CA that issued it and publish the updated,
IACA-signed CRL at the location referenced in your IACA certificate. The customer remains
responsible for the full lifecycle of external certificates, including renewal and revocation. For
more detail, see
[External certificates](/docs/concepts/chain-of-trust#external-certificates).
# API Reference
URL: /docs/issuance/claims-source/api-reference
## Configure a Claims source [#configure-a-claims-source]
## Retrieve all Claim sources [#retrieve-all-claim-sources]
## Retrieve a Claim source [#retrieve-a-claim-source]
## Update a Claim source [#update-a-claim-source]
## Delete a Claim source [#delete-a-claim-source]
# How to configure a Claims source
URL: /docs/issuance/claims-source/guide
## Introduction [#introduction]
A [Claims source](/docs/issuance/claims-source/overview) lets MATTR VII fetch credential claims from your own data system at issuance time. Two configuration decisions shape how well that integration behaves in production: how MATTR VII authorizes with your endpoint, and how it tells your endpoint which record to look up.
This guide walks through the practical configuration patterns for both. It assumes you have already read the [Claims source overview](/docs/issuance/claims-source/overview) and have a backing endpoint that meets the [requirements](/docs/issuance/claims-source/overview#requirements). For an end-to-end walkthrough using a sample app, see the [Claims source tutorial](/docs/issuance/claims-source/tutorial).
## Prerequisites [#prerequisites]
* A publicly accessible HTTPS endpoint that returns claims as JSON.
* An OID4VCI flow (either [Authorization Code](/docs/issuance/authorization-code/overview) or [Pre-authorized Code](/docs/issuance/pre-authorized-code/overview)) in place, so you know which claims are available to map.
* A [Credential configuration](/docs/issuance/credential-configuration/overview) that will reference the claims source.
## Guide overview [#guide-overview]
Configuring a claims source involves three concerns:
1. **[Authorize the request](#authorize-the-request):** Pick the authorization method that matches how your backing system is protected.
2. **[Map request parameters](#map-request-parameters):** Decide which values MATTR VII should pass to your endpoint, and where they come from.
3. **[Error-proof your queries with default values](#error-proof-your-queries-with-default-values):** Make the configuration resilient to missing or unexpected claims.
The examples below use the [Configure a claims source](/docs/issuance/claims-source/api-reference#configure-a-claims-source) endpoint. The same fields are available in the MATTR Portal under **Credential Issuance > Claims sources**.
### Authorize the request [#authorize-the-request]
MATTR VII supports two authorization methods. Choose based on how your data system already protects access, not on which is easier to set up. Reusing your existing access control is almost always the right call.
#### API key [#api-key]
Use `api-key` when your endpoint is protected by a static shared secret passed in a custom header. MATTR VII sends the configured value in an `x-api-key` header on every request.
```json title="API key authorization"
{
"name": "Customer records",
"url": "https://records.example.com/claims",
"authorization": {
"type": "api-key",
"value": "6hrFDATxrG9w14QY9wwnmVhLE0Wg6LIvwOwUaxz761m1J"
},
"requestParameters": {
"customerId": {
"mapFrom": "claims.sub"
}
}
}
```
When this is a good fit:
* You control the endpoint and can place it behind a key-checking gateway.
* The endpoint serves only the claims source use case, so a single shared secret is acceptable.
* You need a quick path to production without standing up an authorization server.
When to avoid it: if your organization already mandates OAuth for service-to-service traffic, do not introduce a parallel key-based path just for claims sources. Rotate the key periodically, and never reuse a key that protects other systems.
MATTR VII does not validate the key when you save the configuration. A typo will not surface until the first issuance attempt, which fails the flow. Test the configuration with a real issuance before rolling it out.
#### OAuth 2.0 client credentials [#oauth-20-client-credentials]
Use `oauth-client-credentials` when your endpoint is protected by an OAuth 2.0 authorization server (Auth0, Okta, Azure AD, Cognito, Keycloak, your own AS). MATTR VII performs the client credentials grant against your `tokenEndpoint`, then attaches the resulting bearer token as an `Authorization: Bearer ` header on every claims source request.
```json title="OAuth client credentials, basic auth method"
{
"name": "Internal CRM",
"url": "https://crm.example.com/api/claims",
"authorization": {
"type": "oauth-client-credentials",
"tokenEndpoint": "https://auth.example.com/oauth/token",
"clientId": "afd16fec-8131-4f0c-8f20-1cd5d67f8e29",
"clientSecret": "1b41186347e4cc716155155cdecbded07536d0f5",
"tokenEndpointAuthMethod": "client_secret_basic",
"audience": "https://crm.example.com",
"scope": "claims:read"
},
"requestMethod": "POST",
"requestParameters": {
"customerId": {
"mapFrom": "claims.sub"
}
}
}
```
The `tokenEndpointAuthMethod` controls how MATTR VII presents the client credentials to your authorization server:
* `client_secret_basic` (default): credentials are sent in a Base64-encoded `Authorization` header on the token request. Use this with most off-the-shelf authorization servers; it is the OAuth 2.0 default.
* `client_secret_post`: credentials are sent in the token request body. Use this only when your authorization server does not accept basic auth on the token endpoint (some legacy or self-hosted servers).
Two optional fields tighten the issued token to the right resource:
* `audience`: set this when your authorization server scopes tokens by audience (Auth0, Azure AD). The value must match what the claims source endpoint expects in the `aud` claim.
* `scope`: set this when your endpoint requires a specific scope (for example `claims:read`). Omit it if your AS issues unscoped service tokens.
When this is a good fit:
* Your data system already lives behind your enterprise identity provider.
* You need centralized credential rotation, revocation, and audit.
* Different downstream systems (claims source, webhooks, internal APIs) should be authorized independently.
MATTR VII fetches a fresh token for each issuance request and does not currently cache tokens between requests. Make sure your authorization server can absorb that traffic, and use a dedicated client for the claims source so rate-limiting and rotation can be scoped to it.
### Map request parameters [#map-request-parameters]
`requestParameters` is the object MATTR VII sends to your endpoint. When `requestMethod` is `GET` (default), each top-level key becomes a query string parameter. When `requestMethod` is `POST`, the object is sent as a JSON request body. The keys you choose define the contract with your endpoint; pick names that match what your endpoint expects to receive.
Each parameter is configured in one of three shapes:
| Shape | Fields | Behavior |
| --------------------- | ---------------------------- | ---------------------------------------------------------------------------------------- |
| Dynamic mapping | `mapFrom` | Resolved at issuance time from the named path. If the path is missing, no value is sent. |
| Dynamic with fallback | `mapFrom` and `defaultValue` | Resolved from the path. If the path is missing, `defaultValue` is used instead. |
| Static value | `defaultValue` | The fixed value is sent on every request, regardless of holder context. |
The rest of this step shows the data sources you can map from and the situations they fit.
#### Map from `claims` [#map-from-claims]
The `claims` object holds every claim gathered during the issuance flow:
* In an Authorization Code flow, this is the union of claims returned by the [Authentication provider](/docs/issuance/authorization-code/authentication-provider/overview) and any claims added by an [Interaction hook](/docs/issuance/authorization-code/interaction-hook/overview).
* In a Pre-authorized Code flow, this is the `claims` object you set when creating the [Credential offer](/docs/issuance/pre-authorized-code/overview).
This is the most common source: it is how you pass a holder-specific lookup key (subject ID, email, employee number, license number) to your endpoint.
```json title="Look up a holder by an authentication provider subject"
{
"requestParameters": {
"subjectId": {
"mapFrom": "claims.sub"
}
}
}
```
```json title="Look up a holder by a domain-specific identifier from an Interaction hook"
{
"requestParameters": {
"employeeNumber": {
"mapFrom": "claims.employee_number"
}
}
}
```
The path is dot-notation, so nested claims work the same way: `claims.profile.country` resolves to the `country` property of the `profile` claim.
The exact paths inside `claims` depend on how each component (Authentication provider, Interaction hook, Credential offer) is configured to expose claims. Inspect the analytics event payload from a test issuance if you are unsure what is available.
#### Map from `authenticationProvider` [#map-from-authenticationprovider]
This object exposes metadata about the Authentication provider used in an Authorization Code flow:
* `authenticationProvider.url`: the configured provider URL.
* `authenticationProvider.subjectId`: the unique identifier of the authenticated user with the provider.
* `authenticationProvider.providerId`: the unique identifier of the provider configuration on your tenant.
Use it when your endpoint expects the canonical subject identifier rather than a claim returned in the ID token, or when the endpoint serves multiple tenants and needs the provider context to route the lookup.
```json title="Use the provider subject as the lookup key"
{
"requestParameters": {
"subjectId": {
"mapFrom": "authenticationProvider.subjectId"
},
"issuer": {
"mapFrom": "authenticationProvider.url"
}
}
}
```
This source is not populated in Pre-authorized Code flows.
#### Map from `credentialConfiguration` [#map-from-credentialconfiguration]
This object exposes the credential configuration being issued:
* `credentialConfiguration.id`: unique configuration identifier.
* `credentialConfiguration.type`: the credential type (for example `org.iso.18013.5.1.mDL`).
* `credentialConfiguration.profile`: the credential format (`mobile` for mDocs, `compact` for CWT, `compact-semantic` for semantic CWT).
This is useful when a single claims source backs several credential types and your endpoint needs to know which record set or schema to return.
```json title="Route the query by credential type"
{
"requestParameters": {
"holderId": {
"mapFrom": "claims.sub"
},
"credentialType": {
"mapFrom": "credentialConfiguration.type"
}
}
}
```
Your endpoint can then branch internally: returning driving privileges for an mDL request and academic transcripts for a study record request, against the same holder ID.
Mapping from `credentialConfiguration` is only useful if your endpoint actually understands the values. The `type` and `profile` strings come straight from your MATTR VII configuration. Coordinate the values with whoever owns the backing data system.
#### Map from `wallet` [#map-from-wallet]
The `wallet` object exposes information about the wallet making the credential request:
* `wallet.id`: the wallet's client identifier. Use it to apply per-wallet logic, for example returning a richer claim set to a wallet you have a direct partnership with.
* `wallet.instanceId`: the unique wallet instance identifier from the attestation JWT. Only populated when [Wallet Attestation](/docs/issuance/credential-issuance/wallet-attestation) is enabled. Use it when your endpoint stores per-device state (renewal counter, device binding, instance-specific entitlements).
The same identifiers are exposed across both OID4VCI and Apple Wallet issuance flows, so a single `wallet` mapping works regardless of which protocol the wallet used to engage the issuer.
```json title="Per-wallet and per-instance lookup"
{
"requestParameters": {
"holderId": {
"mapFrom": "claims.sub"
},
"walletId": {
"mapFrom": "wallet.id"
},
"walletInstanceId": {
"mapFrom": "wallet.instanceId"
}
}
}
```
A legacy `client` object is also available, exposing `client.instanceId` only. It is kept for backwards compatibility with earlier configurations and is on track for removal. Use `wallet` in new configurations and migrate existing `client.instanceId` mappings to `wallet.instanceId`.
#### Map from `issuanceProtocol` [#map-from-issuanceprotocol]
`issuanceProtocol` is a top-level string that tells your endpoint which protocol the wallet engaged the issuer over. Currently the only supported value is `openid4vci`, covering both the [Authorization Code](/docs/issuance/authorization-code/overview) and [Pre-authorized Code](/docs/issuance/pre-authorized-code/overview) flows. [Contact us](mailto:dev-support@mattr.global) if you need support for additional issuance protocols.
Map this parameter when the same credential configuration is offered through multiple protocols and your endpoint needs to vary the returned data based on the channel, for example applying protocol-specific business rules.
```json title="Branch by issuance protocol"
{
"requestParameters": {
"holderId": {
"mapFrom": "claims.sub"
},
"protocol": {
"mapFrom": "issuanceProtocol"
}
}
}
```
#### Static parameters [#static-parameters]
A parameter with only a `defaultValue` is sent on every request, with no mapping. Use it for values that are constant for this claims source but that the endpoint still needs (API version, tenant code, fixed feature flag).
```json title="Pin an API version"
{
"requestParameters": {
"apiVersion": {
"defaultValue": "2024-08"
},
"subjectId": {
"mapFrom": "claims.sub"
}
}
}
```
A `defaultValue` can also be an object or an array, which is useful when the endpoint accepts structured filters in a POST body:
```json title="Static structured value"
{
"requestMethod": "POST",
"requestParameters": {
"filters": {
"defaultValue": {
"includeArchived": false,
"regions": ["AU", "NZ"]
}
},
"subjectId": {
"mapFrom": "claims.sub"
}
}
}
```
#### Combine sources [#combine-sources]
You can mix mappings from different sources in a single configuration. The example below identifies the holder from the authentication provider, scopes the query to the credential type, tags the call with the wallet instance and protocol, and pins an API version:
```json title="Combined parameters"
{
"requestMethod": "POST",
"requestParameters": {
"subjectId": {
"mapFrom": "authenticationProvider.subjectId"
},
"credentialType": {
"mapFrom": "credentialConfiguration.type"
},
"walletInstanceId": {
"mapFrom": "wallet.instanceId"
},
"protocol": {
"mapFrom": "issuanceProtocol"
},
"apiVersion": {
"defaultValue": "2024-08"
}
}
}
```
### Error-proof your queries with default values [#error-proof-your-queries-with-default-values]
Adding a `defaultValue` alongside `mapFrom` keeps issuance running when an expected claim is missing or evaluates to `undefined`. Without a fallback, the parameter is simply omitted from the request, which often causes the endpoint to return a 404 or an empty body, both of which can result in [issuance failures](/docs/issuance/claims-source/overview#failures-and-errors) depending on how your credential configuration is set up.
Two patterns are worth knowing.
#### Fallback to a safe value [#fallback-to-a-safe-value]
When the mapped claim is optional, supply a `defaultValue` that your endpoint can interpret without error. Be careful that the fallback represents the right outcome: for a lookup key, the fallback should match a record your endpoint knows how to handle (often a "no match" placeholder), not a real user.
```json title="Fallback for an optional segment"
{
"requestParameters": {
"subjectId": {
"mapFrom": "claims.sub"
},
"segment": {
"mapFrom": "claims.customer_segment",
"defaultValue": "standard"
}
}
}
```
Here, every holder gets a `segment` value. If the authentication provider did not return one, the endpoint receives `standard` and can apply its default rules.
#### Fallback to a structured value [#fallback-to-a-structured-value]
`defaultValue` accepts strings, arrays, and objects, which is helpful when the endpoint expects a fixed shape:
```json title="Structured fallback"
{
"requestMethod": "POST",
"requestParameters": {
"permissions": {
"mapFrom": "claims.permissions",
"defaultValue": []
},
"preferences": {
"mapFrom": "claims.preferences",
"defaultValue": {
"locale": "en-US",
"format": "standard"
}
}
}
}
```
#### What a fallback cannot do [#what-a-fallback-cannot-do]
A `defaultValue` only fires when the mapping fails or resolves to `undefined`. It does not run when:
* The claims source returns an error (non-2XX status).
* The claims source returns an empty body or a body missing the claim you expected.
* The claims source times out (response longer than three seconds).
For those cases, the lever is in the [Credential configuration](/docs/issuance/credential-configuration/overview#claims-mapping) itself: set a `defaultValue` on optional claim mappings so issuance can complete even if the claims source returns nothing useful. The two layers compose: the claims source `defaultValue` keeps the outbound request well-formed, and the credential configuration `defaultValue` keeps the outbound credential well-formed.
Never use `defaultValue` to mask an authorization problem. If your endpoint can return data for an unauthenticated or unidentified caller (for example because the lookup key fell back to a static value), the wrong holder may receive someone else's claims. Fall back to safe metadata, not to identifiers.
## Best practices [#best-practices]
* **Reuse existing access control.** If your data system already lives behind OAuth, use `oauth-client-credentials`; if it lives behind an API gateway with shared keys, use `api-key`. Do not introduce a parallel auth scheme just for the claims source.
* **Keep parameter names aligned with the endpoint.** The keys in `requestParameters` are the contract with your endpoint. Treat changes as a coordinated deployment, not a configuration tweak.
* **Test with a representative holder.** MATTR VII does not validate authorization values, parameter paths, or the endpoint contract at save time. A test issuance is the only way to confirm the integration works end to end.
* **Stay inside the three-second budget.** The endpoint must respond within three seconds or the issuance fails. Cache, pre-compute, or move heavy work behind asynchronous processing rather than blocking the claims request.
* **Return `{}` when no claims are found.** An empty JSON object lets issuance proceed using credential configuration defaults. Returning a 404 fails the issuance.
* **Audit fallbacks.** Review every `defaultValue` against whether it could mask a real lookup failure for a real holder. Fallbacks are safest for non-identifying metadata.
## What's next? [#whats-next]
* Walk through a complete claims source integration in the [Claims source tutorial](/docs/issuance/claims-source/tutorial).
* Review the full request and response schemas in the [Claims source API reference](/docs/issuance/claims-source/api-reference).
* Learn how returned claims are mapped into the issued credential in the [Credential configuration overview](/docs/issuance/credential-configuration/overview#claims-mapping).
# Claims source
URL: /docs/issuance/claims-source/overview
## Overview [#overview]
Issuers databases are not always integrated with their Authentication providers and might require a
separate integration into the issuance workflow. Claims sources allow configuring your tenant to use
a compatible endpoint to fetch claims and use them in issued credentials.
Your Claims source configuration includes your [authorization](#authorization) credentials for that
service, as well as [request parameters](#request-parameters) that can be used to query it when
fetching data.
Once configured, MATTR VII will make an API request to the claims source using the configured
request parameters and fetch available data as part of the issuance workflow. Any retrieved data
from the claims source can be used in the issued credential.
Claims sources must be explicitly referenced by a [credential configuration](/docs/issuance/credential-configuration/overview) included in the credential offer to be used as part of either an OID4VCI [Authorization Code](/docs/issuance/authorization-code/overview) or [Pre-authorized Code](/docs/issuance/pre-authorized-code/overview) issuance flows.
## Requirements [#requirements]
Each configured claims source must meet the following guidelines:
* Must be accessible from the internet via HTTPS (e.g. `https://example.com`).
* Must expose an endpoint supporting HTTPS request using either GET or POST. This endpoint is what
MATTR VII will call when querying the claims source.
* MATTR VII connects to claims sources using IPv4 only. All requests explicitly use IPv4 to avoid
connection failures with unsupported IPv6 traffic.
## Authorization [#authorization]
MATTR VII supports two authorization methods to interact with your protected claim sources:
* `api-key` : The provided key is passed as a `x-api-key` header in all requests made to the claims
source.
* `oauth-client-credentials` : MATTR VII will first use your oauth client credentials to request an
access token. Once the token is retrieved, it is used as an `Authorization` header prefixed by the
token type in all requests made to the claims source. MATTR VII only supports `Bearer` access
tokens.
## Request parameters [#request-parameters]
Credentials are likely to only require a subset of the data available on your claims source. To
accommodate this, MATTR VII can pass request parameters that can be used to filter data queries.
These parameters can be:
* Any claims retrieved from the
[Authentication provider](/docs/issuance/authorization-code/authentication-provider/overview)
and/or the
[Interaction hook](/docs/issuance/authorization-code/interaction-hook/overview)
components in an
[Authorization Code flow](/docs/issuance/authorization-code/overview).
* Any claims provided as part of the Credential offer in a [Pre-authorized Code flow](/docs/issuance/pre-authorized-code/overview).
* Either dynamic or static.
### Dynamic request parameters [#dynamic-request-parameters]
You can map dynamic request parameters from different objects that are available in the OID4VCI workflow:
* `claims` : This object includes all the claims retrieved from the Authentication provider and/or
the interaction hook.
The path to a specific claim inside the claims object depends on the configuration of the
component the claim was retrieved from (Authentication provider/interaction hook).
* `authenticationProvider` : You can use this object to pass elements retrieved from the authentication
provider as request parameters:
* `url` : URL of the authentication provider.
* `subjectId` : Unique identifier of the user in the authentication provider.
* `providerId` : Unique identifier of the authentication provider.
* `credentialConfiguration` : You can use this object to pass elements of the credential
configuration as request parameters:
* `id` : Unique configuration identifier.
* `type` : Credential type, globally unique per Credential configuration on your tenant
(configuration type is defined as part of their setup process).
* `profile` : Credential format (configuration profile is defined as part of their setup
process). The following options are available:
* mDocs: `mobile`.
* CWT credentials: `compact`.
* Semantic CWT credentials: `compact-semantic`.
* `wallet` : When [Wallet Attestation](/docs/issuance/credential-issuance/wallet-attestation) is enabled, you can use this object to pass the following information as request parameters:
* `id` : Unique identifier of the wallet. In the Attestation JWT, this is provided as `sub`. Related documentation may also refer to `client_id` in the JWT access token, but `sub` is the claim used to identify the wallet application in the attestation.
* `instanceId` : Unique identifier of the wallet instance, as provided in the attestation JWT. This enables
instance-specific data retrieval from the claims source.
* `issuanceProtocol` : Identifies which protocol is used to issue the credential. This allows your claims source to return different data sets based on the issuance protocol in use. The following values are available:
* `openid4vci` : OpenID for Verifiable Credential Issuance flows (Authorization Code and Pre-authorized Code).
* [Contact us](mailto:dev-support@mattr.global) for additional protocol support.
Using dynamic request parameters from the `credentialConfiguration` object relies on structuring
your database query endpoints in a way that can make sense of the `id`, `type` and `profile`
parameters.
The `client` object (exposing `client.instanceId`) is still available for backwards compatibility but is being phased out. Update existing claims source configurations to map from `wallet.instanceId` instead.
To support scenarios where mapping fails or evaluated to `undefined` (attribute doesn’t exist) you
can also provide a default value to use as a fallback parameter. This can be a string, an object or an
array.
### Static request parameters [#static-request-parameters]
You can define a static request parameter by only providing a default value with no mapping path.
Doing so means the default value is always passed as a request parameter, regardless of what claims are
available in a specific issuance workflow.
## Response criteria [#response-criteria]
When a claims source is queried, MATTR VII expects a response that includes claims to be used in the
issued credential. Issuance would fail if any of the following criteria are not met:
* Return a response within three seconds to avoid request timeout.
* Return a 2XX HTTP status code when the request was successful.
* Response body must be a valid JSON, denoted by the application/json MIME type.
* Each key in the JSON object must be a claim name and each value must be a claim value.
* If no claims are found, an empty object must be returned in the response body.
It is recommended to respond with error codes only when something exceptional happens, to avoid [issuance failures](#failures-and-errors).
## Failures and errors [#failures-and-errors]
A claims source integration will result in issuance failures in the following situations:
* Whenever the claims source does not return a 2XX (success) status with a [valid](#response-criteria) response body. Common causes include:
* Network or DNS connection errors.
* Authentication or authorization failures.
* Request timeouts (no response within three seconds).
* Not returning an empty JSON object when no claims are found.
* Any other condition that prevents a successful 2XX response.
* When information returned from the claims source does not satisfy an explicit [credential configuration](/docs/issuance/credential-configuration/overview) requirement (e.g., when no value for a required claim is available and no `defaultValue` is set in the credential configuration). Refer to [claim mapping](/docs/issuance/credential-configuration/overview#claims-mapping) for more information.
# Learn how to integrate a claims source into an OID4VCI workflow
URL: /docs/issuance/claims-source/tutorial
## Introduction [#introduction]
In an [OID4VCI issuance workflow](/docs/issuance/oid4vci-overview), you can enhance the credential issuance
process by configuring your tenant to fetch claims directly from a compatible
[Claims source](/docs/issuance/claims-source/overview). These claims can then be used to issue
verifiable credentials tailored to the user's data.
Claim sources can be integrated both to OID4VCI [Authorization Code](/docs/issuance/authorization-code/overview) and [Pre-authorized Code](/docs/issuance/pre-authorized-code/overview) flows.
This tutorial walks you through setting up an [OID4VCI workflow](/docs/issuance/oid4vci-overview) that issues an ISO-compliant mobile Driving Licence (mDL) by integrating a claims source to retrieve the holder's address and driving privilege information at issuance time. The holder's driver's licence number is used as the query parameter to look up their record in the claims source—mirroring how a real-world issuer would query a DMV or licensing authority database. The tutorial shows you how to achieve this using both the MATTR Portal and the MATTR VII Platform API.
## Prerequisites [#prerequisites]
* Ensure you have completed either the
[Authorization Code](/docs/issuance/authorization-code/tutorial) or [Pre-authorized Code](/docs/issuance/pre-authorized-code/tutorial) tutorials, as the claims source tutorial builds
upon them.
* Download the sample
[Claims source application](https://github.com/mattrglobal/sample-apps/tree/master/claims-source-app),
which will be used to simulate a claims source.
* To test locally you will need to expose your local claims source to the internet. You can do this by setting up a [free ngrok account](https://ngrok.com/), using a Cloudflare tunnel or using your own solution. For this tutorial we will be using ngrok. Sign up for a free account at [ngrok.com](https://ngrok.com/).
We recommend using the MATTR VII [Postman
collection](/docs/api-reference#postman-collection) in this
tutorial. While this isn't an explicit prerequisite it can really speed things up.
## Tutorial overview [#tutorial-overview]
The current tutorial builds upon the [Authorization Code](/docs/issuance/authorization-code/tutorial) or [Pre-authorized Code](/docs/issuance/pre-authorized-code/tutorial) tutorials by adding the following steps:
1. **Set up a local Claims source:** Use the provided sample Node.js application to simulate a
claims source by providing a REST API to retrieve claims for specific users.
2. **Integrate the Claims source with MATTR VII:** Configure MATTR VII to connect to and utilize the
sample Claims source for retrieving claims during an OID4VCI workflow.
The following diagram illustrates how a claims source fits into the OID4VCI issuance workflow:
The workflow proceeds as follows:
1. **Initiate issuance:** The wallet triggers an OID4VCI issuance workflow — either an [Authorization Code](/docs/issuance/authorization-code/overview) or [Pre-authorized Code](/docs/issuance/pre-authorized-code/overview) flow. During this stage, claims may be gathered from an Authentication Provider and/or an Interaction Hook.
2. **Resolve claims source:** MATTR VII reads the [credential configuration](/docs/issuance/credential-configuration/overview) referenced in the credential offer and identifies the configured claims source.
3. **Authorize the request:** MATTR VII authenticates with the claims source using the configured authorization method:
* **OAuth Client Credentials** — MATTR VII requests a Bearer access token from the authorization server and uses it as an `Authorization` header.
* **API Key** — MATTR VII passes the configured key as an `x-api-key` header.
4. **Fetch claims:** MATTR VII sends an HTTP request (GET or POST) to the claims source endpoint, including any configured [request parameters](/docs/issuance/claims-source/overview#request-parameters) — these can be dynamic (mapped from available claims or credential configuration data) or static.
5. **Return claims:** The claims source must respond within three seconds with a `2XX` status code and a valid JSON object containing the claims to be used in the credential. An empty object is returned if no claims are found.
6. **Map and issue:** MATTR VII maps the returned claims according to the credential configuration and generates the signed credential, which is delivered to the wallet.
## Tutorial steps [#tutorial-steps]
### Set up a local Claims source [#set-up-a-local-claims-source]
1. Clone the [MATTR Sample Apps repo](https://github.com/mattrglobal/sample-apps) to your machine
and navigate to the `claims-source-app` folder.
2. Rename the `env-example` file to `.env`.
3. Change the value of `NGROK_AUTHTOKEN` to your ngrok authentication token.
4. Open the `database.json` file and add a new object to represent a user with their mDL record:
* Set the `licenseNumber` value (for example, `DL-123456`). This is the driver's licence number that will be used as the query parameter when looking up the user's record in the claims source.
* If you are building on top of the
[Authorization Code flow tutorial](/docs/issuance/authorization-code/tutorial),
the same value must exist as a property for the authenticated user in your Auth0 application.
* If you are building on top of the
[Pre-authorized Code flow tutorial](/docs/issuance/pre-authorized-code/tutorial),
you can use any value, but make note of it as you will need it later when you
create the credential offer.
* Populate the following mDL address fields with realistic test values:
* `resident_address` — street address (for example, `123 Main Street`)
* `resident_city` — city (for example, `Springfield`)
* `resident_state` — state or region code (for example, `IL`)
* `resident_postal_code` — postal code (for example, `62701`)
5. Start the claims source app either via npm or docker:
```bash title="Start via npm"
npm install
npm run dev
```
or
```bash title="Start via docker"
docker compose up --build
```
6. Make note of the `Public Claims Source URL` displayed in the terminal after starting the app.
This URL will be used to configure the Claims source in your MATTR VII tenant.
Your claims source is now set up and ready to be used. The provided application meets the
[requirements](/docs/issuance/claims-source/overview#requirements) for
integrating a claims source into a MATTR VII OID4VCI workflow, and will return the holder's resident address details when queried with their driver's licence number.
Next, we will configure MATTR VII to connect to this claims source.
### Configure the Claims source in your MATTR VII tenant [#configure-the-claims-source-in-your-mattr-vii-tenant]
This step can be achieved either via the MATTR Portal or the MATTR VII Platform APIs.
1. In the navigation panel on the left-hand side, expand the **Credential Issuance** menu.
2. Select **Claims sources**.
3. Click the **Create new** button.
4. Insert a meaningful name for your Claims source in the *Name* field. For example, "DMV Claims source".
5. In the *URL* field, paste the `Public Claims Source URL` generated when you started the claims
source application.
6. In the *Authorization type* section, select **API Key** as the type.
7. In the *API Key* field, paste `supersecretapikey`. This is the API key used by the sample claims
source application to control access. In Production environments, it is strongly recommended to
use a more secure API key.
8. Paste the following code into the *Request parameters* field:
```json title="Request parameters"
{
"licenseNumber": {
"mapFrom": "claims.licenseNumber"
}
}
```
This maps the holder's driver's licence number to a query parameter that will be sent to the claims source to look up their mDL record.
* In Authorization code flows the `licenseNumber` claim must be provided by the Authentication provider. It can be retrieved from the authenticated user's identity in an Interaction hook and added to the claim set that is returned to MATTR VII, or it can be included in the ID token claims by the Authentication provider (e.g., Auth0) and mapped in the credential configuration.
* In Pre-authorized code flows it is provided by the issuer as part of the credential offer. In this case, you would map it from the credential offer claims in the same way as shown above.
9. Select **Create** to create the Claims source.
Make a request of the following structure to
[configure a new claims source](/docs/issuance/claims-source/api-reference#configure-a-claims-source):
```http title="Request"
POST /v1/claim-sources
```
```json title="Request body"
{
"name": "DMV Claims source",
"url": "", // [!code highlight]
"authorization": { // [!code highlight]
"type": "api-key", // [!code highlight]
"value": "supersecretapikey" // [!code highlight]
},
"requestMethod": "GET",
"requestParameters": { // [!code highlight]
"licenseNumber": { // [!code highlight]
"mapFrom": "claims.licenseNumber" // [!code highlight]
} // [!code highlight]
}
}
```
* `url` : Replace this with the `Public Claims Source URL` generated when you started the claims
source application. The URL should follow this format: `https:///claims`.
* `authorization` : Specifies how to access the claims source. The sample claims source application
uses an API key (`supersecretapikey`) for access control. For production environments, it is
strongly recommended to use a more secure API key.
* `requestParameters` : Maps the holder's driver's licence number to a `licenseNumber` query parameter. MATTR VII sends this parameter to the claims source to look up the holder's mDL record, returning their resident address and other mDL fields.
* In Authorization code flows the `licenseNumber` claim must be provided by the Authentication provider. It can be retrieved from the authenticated user's identity in an Interaction hook and added to the claim set that is returned to MATTR VII, or it can be included in the ID token claims by the Authentication provider (e.g., Auth0) and mapped in the credential configuration.
* In Pre-authorized code flows it is provided by the issuer as part of the credential offer. In this case, you would map it from the credential offer claims in the same way as shown above.
**Response**
```json title="Response body"
{
"id": "945214ad-3635-4aff-b51d-61d69a3c8eee" // [!code focus]
// Remaining response properties
}
```
* `id` : Unique identifier for the configured Claims source. We will use it in the next step to
integrate the Claims source into the OID4VCI workflow.
### Use the Claim source in a credential configuration [#use-the-claim-source-in-a-credential-configuration]
This step can be achieved either via the MATTR Portal or the MATTR VII Platform APIs.
1. In the navigation panel on the left-hand side, expand the **Credential Issuance** menu.
2. Select **mDocs**.
3. Select the **Create new** button.
4. In the *Name* text box, enter a clear and descriptive title that will appear on the credential in
the wallet, for example "Mobile Driving Licence".
5. In the *Description* text box, enter a clear and descriptive description that will appear on the
credential in the wallet, for example "ISO 18013-5 compliant mDL".
6. In the *Credential type* text box, enter `org.iso.18013.5.1.mDL`.
7. Paste the following JSON into the *Claim mappings* text box:
```json title="Claim mappings"
{
"org.iso.18013.5.1": {
"family_name": {
"mapFrom": "claims.family_name",
"type": "string"
},
"given_name": {
"mapFrom": "claims.given_name",
"type": "string"
},
"document_number": {
"mapFrom": "claims.licenseNumber",
"type": "string"
},
"resident_address": {
"mapFrom": "claims.resident_address",
"type": "string"
},
"resident_city": {
"mapFrom": "claims.resident_city",
"type": "string"
},
"resident_state": {
"mapFrom": "claims.resident_state",
"type": "string"
},
"resident_postal_code": {
"mapFrom": "claims.resident_postal_code",
"type": "string"
}
}
}
```
The address claims (`resident_address`, `resident_city`, `resident_state`, `resident_postal_code`) will be retrieved from the Claims source configured in the previous step. The identity claims (`family_name`, `given_name`) will be retrieved from the authentication provider (e.g., Auth0), and `document_number` is mapped from the `licenseNumber` claim.
Note that a compliant mDL credential has specific requirements for the claims it includes. In this tutorial we are including the `resident_address`, `resident_city`, `resident_state` and `resident_postal_code` claims from the Claims source, and the `family_name`, `given_name` and `document_number` claims from the authentication provider, to demonstrate how to map claims from multiple sources to meet these requirements.
8. Use the *Claim source* dropdown to select the Claims source you created in the previous step.
This will allow the credential to retrieve claims from the configured Claims source during
issuance.
9. Enter "1" in the *Months* text box in the *Validity for* panel to set the credential expiration
period.
10. Select the **Create** button to create the credential configuration.
Make the following request to create an ISO 18013-5 compliant
[mDoc credentials configuration](/docs/issuance/credential-configuration/api-reference/mdocs#create-an-mdocs-credential-configuration)
that includes the holder's identity claims from the authentication provider, and their resident address retrieved from your configured Claims source:
```http title="Request"
POST /v2/credentials/mobile/configurations
```
```json title="Request body"
{
"type": "org.iso.18013.5.1.mDL",
"expiresIn": {
"months": 1
},
"claimMappings": {
"org.iso.18013.5.1": {
"family_name": {
"mapFrom": "claims.family_name",
"type": "string"
},
"given_name": {
"mapFrom": "claims.given_name",
"type": "string"
},
"document_number": {
"mapFrom": "claims.licenseNumber",
"type": "string"
},
"resident_address": { // [!code highlight]
"mapFrom": "claims.resident_address", // [!code highlight]
"type": "string" // [!code highlight]
}, // [!code highlight]
"resident_city": { // [!code highlight]
"mapFrom": "claims.resident_city", // [!code highlight]
"type": "string" // [!code highlight]
}, // [!code highlight]
"resident_state": { // [!code highlight]
"mapFrom": "claims.resident_state", // [!code highlight]
"type": "string" // [!code highlight]
}, // [!code highlight]
"resident_postal_code": { // [!code highlight]
"mapFrom": "claims.resident_postal_code", // [!code highlight]
"type": "string" // [!code highlight]
} // [!code highlight]
}
},
"branding": {
"name": "Mobile Driving Licence",
"description": "ISO 18013-5 compliant mDL",
"backgroundColor": "#1a3a5cff"
},
"claimSourceId": "", // [!code highlight]
"includeStatus": true
}
```
* `resident_address`, `resident_city`, `resident_state`, `resident_postal_code` : These claims represent the holder's residential address. They are retrieved from the Claims source using the holder's `licenseNumber` as the lookup key, and stored in the `claims` object. During credential issuance they are mapped into the `org.iso.18013.5.1` namespace of the mDL.
Note that a compliant mDL credential has specific requirements for the claims it includes. In this tutorial we are including the `resident_address`, `resident_city`, `resident_state` and `resident_postal_code` claims from the Claims source, and the `family_name`, `given_name` and `document_number` claims from the authentication provider, to demonstrate how to map claims from multiple sources to meet these requirements.
* `claimSourceId` : Replace this with the `id` value returned in the response when you configured
the Claims source in the previous step.
*Response*
```json title="Response body"
{
"id": "294868aa-3814-4a50-9862-5ff48381a8e5" // [!code focus]
//... rest of your credential configuration
}
```
* `id` : Unique identifier for the created mDocs credentials configuration. This ID is used to
create a Credential offer in the next step.
### Create a Credential offer [#create-a-credential-offer]
You now have all the pieces in place and can wrap them all together to generate a
[Credential offer](/docs/issuance/credential-offer/overview) and share it with the intended holder.
Make sure you create a credential offer that matches the flow you are implementing.
Creating pre-authorized code flow credential offers in the MATTR Portal is for testing purposes only. The populated user ID is generated by the MATTR Portal and cannot be changed to another user later.
1. In the navigation panel on the left-hand side, expand the **Credential Issuance** menu.
2. Select **Credential offer**.
3. Use the *Workflow* radio button to select **Pre-authorized code flow**.
4. Select the **Select** button.
5. Check the checkbox next to the credential configuration you created in the previous step.
6. Select the **Apply** button.
7. In the Claims panel, select **Add new** and add a claim with the following details:
* Attribute: `licenseNumber`
* Type: `string`
* Value: The driver's licence number you used when you
[set up your local claims source](#set-up-a-local-claims-source) (for example, `DL-123456`).
8. Select the **Generate** button.
9. Copy the Transaction code that is displayed on the screen.\
In production deployments you will need to provide it to the user through a different channel
(e.g., email, SMS).
10. Download the displayed QR code.\
This QR code will be used by the holder to claim the credential.
Make the following request to
[generate a new Pre-authorized Code flow Credential offer](/docs/issuance/pre-authorized-code/api-reference#create-credential-offer):
```http title="Request"
POST /v1/openid/offers/pre-authorized
```
```json title="Request body"
{
"credentials": ["707e920a-f342-443b-ae24-6946b7b5033e"], // [!code highlight]
"transactionCodeConfiguration": {
"inputMode": "numeric",
"description": "Please enter the one-time code that was sent to you."
},
"claims": {
"family_name": "Doe",
"given_name": "Jane",
"licenseNumber": "" // [!code highlight]
},
"expiresIn": {
"minutes": 10
}
}
```
* `credentials` : Populate the array with the `id` element returned in the response when you created
an mDocs credentials configuration in the previous step.
* `licenseNumber` : Replace `` with the driver's licence number you used when you
[set up your local claims source](#set-up-a-local-claims-source) (for example, `DL-123456`). MATTR VII will use this value to query the claims source and retrieve the holder's resident address.
In this example we are manually inserting the driver's licence number into the credential offer request. In a
real-world scenario, you would want to dynamically populate this value based on the
authenticated user's identity, for example by retrieving it from your identity store.
*Response*
```json title="Response body"
{
"id": "e6e5e43c-8053-464a-aca4-ca43da765c97",
"uri": "openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Flearn.vii.au01.mattr.global%22%2C%22credentials%22%3A%5B%22b7b380be-1467-446b-8683-1d131e6532be%22%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%229K3N9UPQD-Hs5f5-gPx0fRJEmb1XTZsWszzXL024pV0%22%2C%22tx_code%22%3A%7B%22length%22%3A6%2C%22input_mode%22%3A%22numeric%22%2C%22description%22%3A%22Please%20enter%20the%20one-time%20code%20that%20was%20sent%20to%20you%20via%20email.%22%7D%7D%7D%7D", // [!code focus]
"userId": "6e30dd69-c867-4279-afd3-e6619253b4a4",
"expiresAt": "2025-08-25T01:39:00.523Z",
"transactionCode": "763677" // [!code focus]
}
```
You will need to obtain two important elements from the response:
* `transactionCode` : This is the one-time code that the user will need to provide in order to claim
the credential. In production deployments you will need to provide it to the user through a
different channel (e.g., email, SMS).
* `uri` : This URI can be used by a digital wallet to trigger the OID4VCI workflow. Use one of the
following tools to convert the `uri` value to a QR code (make sure you use the `Plain text` option
where available) and save the generated QR code as an image file.
* [https://www.the-qrcode-generator.com/](https://www.the-qrcode-generator.com/)
* [http://goqr.me/api/](http://goqr.me/api/)
* [https://www.qr-code-generator.com/](https://www.qr-code-generator.com/)
MATTR is not affiliated with any of these service providers and cannot vouch for their
offerings.
1. In the navigation panel on the left-hand side, expand the **Credential Issuance** menu.
2. Select **Credential offer**.
3. Select the **Select** button.
4. Check the checkbox next to the credential configuration you created in the previous step.
5. Select the **Apply** button.
6. Select the **Generate** button.
7. Download the generated QR code by selecting the **Download** button.
Make the following request to
[generate a new Credential offer](/docs/issuance/authorization-code/api-reference#create-credential-offer):
```http title="Request"
POST /v1/openid/offers
```
```json title="Request body"
{
"credentials": [""]
}
```
* `credentials`: Replace this with the `id` value returned in the response when you created the
mDocs credentials configuration in the previous step.
*Response*
The response will include a `uri` element which can be used by a digital wallet to trigger the
OID4VCI workflow. Use one of the following tools to convert the `uri` value to a QR code (make sure
you use the `Plain text` option where available):
* [https://www.the-qrcode-generator.com/](https://www.the-qrcode-generator.com/)
* [http://goqr.me/api/](http://goqr.me/api/)
* [https://www.qr-code-generator.com/](https://www.qr-code-generator.com/)
MATTR is not affiliated with any of these service providers and cannot vouch for their
offerings.
Save the generated QR code on your computer.
### Test the workflow [#test-the-workflow]
1. Open the GO hold example app.
2. Select **Scan**.
3. Scan the QR code generated in the previous step.
4. Review the credential offer and select **Accept**.
5. Follow the issuance workflow instructions to claim the credential.
You should now see an mDL in your wallet that includes the holder's resident address (`resident_address`, `resident_city`, `resident_state`, `resident_postal_code`), which was retrieved from your configured Claims source. MATTR VII obtained these claims by querying the Claims source using the holder's driver's licence number as a query parameter—either supplied by the authentication provider (Authorization Code flow) or specified in the credential offer (Pre-authorized Code flow).
## What's next? [#whats-next]
Check out more resources on MATTR Learn that will enable you to:
* Configure an [Interaction hook](/docs/issuance/authorization-code/interaction-hook/tutorial) to redirect the user to
custom components as part of the issuance workflow (only supported for the [Authorization Code flow](/docs/issuance/authorization-code/overview)).
* Apply branding to issued credentials as part of creating a
[Credential configuration](/docs/issuance/credential-configuration/overview).
# Create a Credential configuration
URL: /docs/issuance/credential-configuration/guide
## Introduction [#introduction]
In an [OID4VCI](/docs/issuance/oid4vci-overview) issuance workflow,
[Credential offers](/docs/issuance/credential-offer/overview) are created based on
[Credential configurations](/docs/issuance/credential-configuration/overview). These are templates
that define rules and parameters that control the structure and content of the credentials being
issued.
The purpose of this tutorial is to demonstrate how to create a Credential configuration using
the MATTR Portal or via API calls.
## Prerequisites [#prerequisites]
* Ensure you have completed either the
[Authorization Code](/docs/issuance/authorization-code/tutorial) or [Pre-authorized Code](/docs/issuance/pre-authorized-code/tutorial) tutorials, as the claims source tutorial builds
upon them.
* Review the [Credential configuration overview](/docs/issuance/credential-configuration/overview)
page for a detailed explanation of the key concepts and considerations involved in creating a
credential configuration.
We recommend using the MATTR VII [Postman
collection](/docs/api-reference#postman-collection) in this
tutorial. While this isn't an explicit prerequisite it can really speed things up.
## Guide overview [#guide-overview]
When planning any credential configuration, an issuer should take the following factors into
account:
* **Credential type:** Used to uniquely identify credentials issued via this configuration.
* **Credential content:** Decide what information will be included in the credential, and how it will be
structured.
* **Credential validity:** Set the appropriate validity period to reflect how long the credential should
be considered accurate and trustworthy.
* **Revocation support:** Determine whether the credential needs to support revocation status updates.
* **Branding configuration:** Specify how the credential should appear in a digital wallet. While
display conventions are not yet standardized, this tutorial uses MATTR’s proprietary format to
support branding, clarity, and holder trust.
This tutorial will walk you through each of these steps and explain the different considerations,
options, and best practices to help you create a well-formed credential configuration.
### Credential type [#credential-type]
The credential type is used to differentiate between different types of credentials. It is a unique
identifier that specifies the purpose and structure of the credential being issued.
It is set using the
[`type`](/docs/issuance/credential-configuration/api-reference/mdocs#create-an-mdocs-credential-configuration!path=type\&t=request)
property when creating a credential configuration:
```json title="Setting Credential Type"
{
"type": "org.iso.18013.5.1.mDL"
// Rest of the credential configuration
}
```
Refer to the [Credential configuration
overview](/docs/issuance/credential-configuration/overview#credential-type) for considerations
and best practices on defining credential types.
### Credential content [#credential-content]
The credential content defines the specific information that will be included in the credential.
This can include various claims about the holder, such as their name, email address, and any other
relevant data.
The content is set using the claimMappings object, as shown in the following example:
```json title="Setting Credential Content"
{
"claimMappings": {
"com.example.personaldetails.1": {
"name": {
"mapFrom": "claims.name",
"type": "string",
"required": true
},
"email": {
"mapFrom": "claims.email",
"type": "string",
"defaultValue": "Not provided"
}
}
},
"claimSourceId": "78e1b90c-401d-45bb-89c0-938da4d44c60"
// Rest of the credential configuration
}
```
This example maps claims into a single namespace (`com.example.personaldetails.1`) and defines two
claims within it:
* `name` : This claim is mapped from the `claims.name` property in the claim source. It is of type
`string` and is marked as `required`, meaning that the issuance process will fail if this claim is
not provided.
* `email` : This claim is also mapped from the `claims.email` property in the claim source. It is of
type `string` but is not marked as required. If the claim is not provided, it will default to the
value "Not provided".
The `claimSourceId` property specifies the [claims source](/docs/issuance/claims-source/overview)
from which claims are dynamically retrieved as part of the issuance flow.
Each claim can also include an optional `display` array to provide localized labels that wallets
can render in the holder's preferred language. For example, to label the `name` claim above in both
English and German:
```json title="Claim with localized display labels"
{
"name": {
"mapFrom": "claims.name",
"type": "string",
"required": true,
"display": [
{ "name": "Full Name", "locale": "en-US" },
{ "name": "Vollständiger Name", "locale": "de-DE" }
]
}
}
```
When configured, these labels are surfaced in the issuer metadata at
`/.well-known/openid-credential-issuer`. `locale` values must be unique within a given claim's
`display` array, and `display` is currently supported for mDoc credential configurations only.
Refer to the [Credential configuration
overview](/docs/issuance/credential-configuration/overview#credential-content) for considerations
and best practices on defining and mapping credential content, including
[localized display labels](/docs/issuance/credential-configuration/overview#localized-display-labels).
### Credential validity [#credential-validity]
Credential validity defines the period during which the credential is considered valid and can be
used for its intended purpose. This is set using a combination of three properties in the credential
configuration. Examples include:
```json
{
"expiresIn": {
"days": 30
}
// Rest of the credential configuration
}
```
In this example we are setting the credential to expire 30 days after issuance.
```json
{
"validFrom": {
"defaultValue": "2023-01-01T00:00:00Z"
},
"validUntil": {
"defaultValue": "2023-02-01T23:59:59Z"
}
// Rest of the credential configuration
}
```
Note that in this example `expiresIn` must also be set, as it is a required property. Its value
will be ignored as both `validFrom` and `validUntil` are provided.
Refer to the [Credential configuration
overview](/docs/issuance/credential-configuration/overview#credential-validity) for
considerations and best practices on defining and mapping credential validity.
### Revocation support [#revocation-support]
MATTR VII allows you to update the status of issued credentials, enabling you to [revoke](/docs/issuance/revocation/overview) or suspend
credentials when necessary. Verifiers can check the status of a presented credential without
compromising the holder’s privacy.
Support for credential revocation is configured as part of the credential configuration, using the
[`includeStatus`](/docs/issuance/credential-configuration/api-reference/mdocs#create-an-mdocs-credential-configuration!path=includeStatus\&t=request)
boolean:
```json title="Revocation Configuration"
{
"includeStatus": true
// Rest of credential configuration
}
```
* `includeStatus` : When set to `true`, issued credentials are revocable. They'll include a `status`
object, which refers a [Status list](/docs/issuance/revocation/overview#status-list) where the credential
status is indicated.
### Credential branding [#credential-branding]
Credential configurations can include information regarding how to brand them when they are
displayed in a digital wallet. This is useful for enhancing the user experience and ensuring that
the credential is easily recognizable.
While display conventions are not yet standardized, this tutorial uses MATTR’s proprietary
format to support branding, clarity, and holder trust.
Branding is controlled via the following properties of the
[`branding`](/docs/issuance/credential-configuration/api-reference/mdocs#create-an-mdocs-credential-configuration!path=branding\&t=request)
object:
```json title="Branding Configuration"
{
"branding": {
"name": "Course credential",
"description": "Diploma in Management",
"backgroundColor": "#860012",
"watermarkImage": "https://example-path-to-image-data.com",
"issuerLogo": "data:image/png;base64,anything",
"issuerIcon": "data:image/svg+xml;base64,anything"
}
// Rest of credential configuration
}
```
* `name` : Displayed on the top part of the credential.
* `description` : Displayed below the name field.
* `backgroundColor` : Applied as the credential background color.
* `watermarkImage` : Displayed as a pattern on top of the credential.
* `issuerLogo` : Displayed on the bottom part of the credential.
* `issuerIcon` : Displayed next to the issuer’s name.
Refer to the [API
Reference](/docs/issuance/credential-configuration/api-reference/mdocs#create-an-mdocs-credential-configuration!path=branding\&t=request)
for requirements and best practices for each of these elements.
### Putting it all together [#putting-it-all-together]
**Step 1: Create a Credential configuration**
1. In the navigation panel on the left-hand side, expand the **Credential Issuance** menu.
2. Select **mDocs**.
3. Select the **Create new** button.
4. In the *Name* text box, enter a clear and descriptive title that will appear on the credential in
the wallet, for example "My Credential Configuration Tutorial Credential".
5. In the *Description* text box, enter a clear and descriptive description that will appear on the
credential in the wallet, for example "Credential Configuration Best Practices".
6. In the *Credential type* text box, enter a unique identifier for the credential type, for example
`com.example.credentialconfiguration.2`.
7. Copy and paste the following JSON into the *Claim mappings* text box:
```json title="Claim mappings"
{
"com.example.personaldetails.1": {
"name": {
"mapFrom": "claims.name",
"type": "string",
"required": true
},
"email": {
"mapFrom": "claims.email",
"type": "string",
"required": true
},
"age": {
"mapFrom": "claims.age",
"type": "number"
}
}
}
```
8. Enter "1" in the *Months* text box in the *Validity for* panel to set the credential expiration
period.
9. Select the **Create** button to create the credential configuration.
**Step 2: Create a Credential offer**
You can now generate a [Credential offer](/docs/issuance/credential-offer/overview) and share it with
the intended holder.
Make sure you create a credential offer that matches the flow you are implementing.
Creating pre-authorized code flow credential offers in the MATTR Portal is for testing purposes only. The populated user ID is generated by the MATTR Portal and cannot be changed to another user later.
1. In the navigation panel on the left-hand side, expand the **Credential Issuance** menu.
2. Select **Credential offer**.
3. Use the *Workflow* radio button to select **Pre-authorized code flow**.
4. Select the **Select** button.
5. Check the checkbox next to the credential configuration you created in the previous step.
6. Select the **Apply** button.
7. In the Claims panel, select **Add new** and add the following claims:
* Attribute: `name`
* Type: `string`
* Value: You can use any value here. It will be mapped to the `name` claim in the issued credential.
* Attribute: `email`
* Type: `string`
* Value: You can use any value here. It will be mapped to the `email` claim in the issued credential, such as the email used when you created the claim source (if applicable) or any email you want if you are not using a claim source.
8. Select the **Generate** button.
9. Copy the Transaction code that is displayed on the screen.\
In production deployments you will need to provide it to the user through a different channel
(e.g., email, SMS).
10. Download the displayed QR code.\
This QR code will be used by the holder to claim the credential.
1. In the navigation panel on the left-hand side, expand the **Credential Issuance** menu.
2. Select **Credential offer**.
3. Use the *Workflow* radio button to select **Authorization code flow**.
4. Select the **Select** button.
5. Check the checkbox next to the credential configuration you created in the previous step.
6. Select the **Apply** button.
7. Select the **Generate** button.
8. Download the displayed QR code.\
This QR code will be used by the holder to claim the credential.
**Step 1: Create a Credential configuration**
Make the following request to create an
[mDoc credentials configuration](/docs/issuance/credential-configuration/api-reference/mdocs#create-an-mdocs-credential-configuration):
```http title="Request"
POST /v2/credentials/mobile/configurations
```
```json title="Request body"
{
"type": "com.example.credentialconfiguration.2",
"expiresIn": {
"months": 1
},
"claimMappings": {
"com.example.personaldetails.1": {
"name": {
"mapFrom": "claims.name",
"type": "string",
"required": true
},
"email": {
"mapFrom": "claims.email",
"type": "string",
"required": true
},
"age": {
"mapFrom": "claims.age",
"type": "number"
}
}
},
"branding": {
"name": "My Credential Configuration Tutorial Credential",
"description": "Credential Configuration Best Practices",
"backgroundColor": "#d82db3ff"
},
"claimSourceId": "",
"includeStatus": true
}
```
* `claimSourceId` : If you completed the
[Claim source tutorial](/docs/issuance/claims-source/tutorial), you can replace this with the ID
of the claim source you created. Otherwise, omit this line from the request body.
*Response*
```json title="Response body"
{
"id": "294868aa-3814-4a50-9862-5ff48381a8e5"
//... rest of your credential configuration
}
```
* `id` : Unique identifier for the created mDocs credentials configuration. This ID is used to
create a Credential offer in the next step.
**Step 2: Create a Credential offer**
You can now generate a [Credential offer](/docs/issuance/credential-offer/overview) and share it with
the intended holder.
Make sure you create a credential offer that matches the flow you are implementing.
Make the following request to
[generate a new Pre-authorized Code flow Credential offer](/docs/issuance/pre-authorized-code/api-reference#create-credential-offer):
```http title="Request"
POST /v1/openid/offers/pre-authorized
```
```json title="Request body"
{
"credentials": ["707e920a-f342-443b-ae24-6946b7b5033e"], // [!code highlight]
"transactionCodeConfiguration": {
"inputMode": "numeric",
"description": "Please enter the one-time code that was sent to you via email."
},
"claims": {
"name": "John Doe", // [!code highlight]
"email": "" // [!code highlight]
},
"expiresIn": {
"minutes": 10
}
}
```
* `credentials` : Populate the array with the `id` element returned in the response when you created
an mDocs credentials configuration in the previous step.
* `name` : You can use any value here. It will be mapped to the `name` claim in the issued
credential.
* `email` : You can use any value here. It will be mapped to the `email` claim in the issued
credential.
In this example we are manually inserting the `email` and `age` claims into the credential offer
request. In a real-world scenario, you would want to dynamically populate this value based on
the authenticated user.
*Response*
```json title="Response body"
{
"id": "e6e5e43c-8053-464a-aca4-ca43da765c97",
"uri": "openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Flearn.vii.au01.mattr.global%22%2C%22credentials%22%3A%5B%22b7b380be-1467-446b-8683-1d131e6532be%22%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%229K3N9UPQD-Hs5f5-gPx0fRJEmb1XTZsWszzXL024pV0%22%2C%22tx_code%22%3A%7B%22length%22%3A6%2C%22input_mode%22%3A%22numeric%22%2C%22description%22%3A%22Please%20enter%20the%20one-time%20code%20that%20was%20sent%20to%20you%20via%20email.%22%7D%7D%7D%7D", // [!code focus]
"userId": "6e30dd69-c867-4279-afd3-e6619253b4a4",
"expiresAt": "2025-08-25T01:39:00.523Z",
"transactionCode": "763677" // [!code focus]
}
```
You will need to obtain two important elements from the response:
* `transactionCode` : This is the one-time code that the user will need to provide in order to claim
the credential. In production deployments you will need to provide it to the user through a
different channel (e.g., email, SMS).
* `uri` : This URI can be used by a digital wallet to trigger the OID4VCI workflow. Use one of the
following tools to convert the `uri` value to a QR code (make sure you use the `Plain text` option
where available) and save the generated QR code as an image file.
* [https://www.the-qrcode-generator.com/](https://www.the-qrcode-generator.com/)
* [http://goqr.me/api/](http://goqr.me/api/)
* [https://www.qr-code-generator.com/](https://www.qr-code-generator.com/)
MATTR is not affiliated with any of these service providers and cannot vouch for their
offerings.
Make the following request to
[generate a new Credential offer](/docs/issuance/authorization-code/api-reference#create-credential-offer):
```http title="Request"
POST /v1/openid/offers
```
```json title="Request body"
{
"credentials": [""]
}
```
* `credentials`: Replace this with the `id` value returned in the response when you created the
mDocs credentials configuration in the previous step.
*Response*
The response will include a `uri` element which can be used by a digital wallet to trigger the
OID4VCI workflow. Use one of the following tools to convert the `uri` value to a QR code (make sure
you use the `Plain text` option where available):
* [https://www.the-qrcode-generator.com/](https://www.the-qrcode-generator.com/)
* [http://goqr.me/api/](http://goqr.me/api/)
* [https://www.qr-code-generator.com/](https://www.qr-code-generator.com/)
MATTR is not affiliated with any of these service providers and cannot vouch for their
offerings.
Save the generated QR code on your computer.
## Test the workflow [#test-the-workflow]
1. Open the GO hold example app.
2. Select **Scan**.
3. Scan the QR code generated in the previous step.
4. Review the credential offer and select **Accept**.
5. Follow the issuance workflow instructions to claim the credential.
* If you are testing an Authorization Code flow, you will be redirected to the
configured authentication provider to authenticate. After a successful authentication you will
be redirected back to the GO hold app to continue the issuance workflow.
* If you are testing a Pre-authorized Code flow, you will also be prompted to enter the
`transactionCode` value returned in the response when you created the credential offer.
You should now have a new credential your MATTR GO Hold app.
If you are using a claim source as part of your credential configuration, ensure that it is
correctly set up and accessible. Issuance may fail if the claims source is not reachable or does
not return the expected claims.
## What's next? [#whats-next]
Check out more resources on MATTR Learn that will enable you to:
* Configure a [Claims source](/docs/issuance/claims-source/tutorial) to retrieve data from
compatible data sources and use it in the issued credential.
* Configure an [Interaction hook](/docs/issuance/authorization-code/interaction-hook/tutorial) to redirect the user to custom
components as part of the issuance workflow.
# ISO-compliant credentials
URL: /docs/issuance/credential-configuration/iso-compliant-credentials
Description: Learn how to create credential configurations that result in ISO-compliant credentials
This guide helps you create credential configurations that comply with specific ISO standards for mobile credentials (mDocs). It provides detailed information about the namespaces, data elements, and requirements for issuing standards-compliant digital credentials.
The guide covers:
* **ISO/IEC 18013-5:2021:** Mobile Driving License (mDL) standard, including mandatory and optional data elements
* **ISO/IEC TS 23220-2:2024:** Reusable namespaces and claims for mobile documents
Each section includes complete claim definitions, compliance requirements, and practical examples to help you implement ISO-compliant credential issuance.
## ISO/IEC 18013-5:2021 namespace and claims [#isoiec-18013-52021-namespace-and-claims]
ISO/IEC 18013-5:2021 is an international standard that defines the mobile Driving License (mDL) —
a digital representation of a physical driver's license. It specifies the data elements, security
features, and communication protocols for issuing and verifying mDL credentials. The standard
defines a namespace `org.iso.18013.5.1` containing mandatory and optional data elements that can be
included in an mDL credential.
The example below shows a `claimMappings` object you can use in your credential configuration to issue ISO/IEC 18013-5:2021 compliant mDL credentials. This example includes all the mandatory and commonly used optional claims defined in Table 5, Section 7.2 of the standard. In practice, you would select only the claims that are relevant to your use case and comply with local regulations:
```json title="Example claimMappings object for ISO/IEC 18013-5:2021 compliance"
{
"org.iso.18013.5.1": {
"family_name": {
// Last name, surname, or primary identifier of the mDL holder (mandatory)
"type": "string",
"mapFrom": "claims.family_name"
},
"given_name": {
// First name(s) or other name(s) of the mDL holder (mandatory)
"type": "string",
"mapFrom": "claims.given_name"
},
"birth_date": {
// Date of birth of the mDL holder in full-date format as defined in RFC 3339 (mandatory)
"type": "date",
"mapFrom": "claims.birth_date"
},
"issue_date": {
// Date when the mDL was issued in full-date format as defined in RFC 3339 (mandatory)
"type": "date",
"mapFrom": "claims.issue_date"
},
"expiry_date": {
// Date when the mDL will expire in full-date format as defined in RFC 3339 (mandatory)
"type": "date",
"mapFrom": "claims.expiry_date"
},
"issuing_country": {
// Alpha-2 country code as defined in ISO 3166-1 of the issuing authority's country (mandatory)
// Must match the country of the IACA at the root of the certificate chain that signs the mDL (see callout below)
"type": "string",
"defaultValue": "US"
},
"issuing_authority": {
// Issuing authority name (mandatory)
"type": "string",
"defaultValue": "State Department of Motor Vehicles"
},
"document_number": {
// The number assigned or calculated by the issuing authority (mandatory)
"type": "string",
"mapFrom": "claims.document_number"
},
"portrait": {
// Portrait image of the mDL holder (mandatory)
"type": "binary",
"mapFrom": "claims.portrait"
},
"driving_privileges": {
// Driving privileges of the mDL holder (mandatory)
// Array of objects containing vehicle category code, issue date, expiry date, codes, etc.
"type": "array",
"mapFrom": "claims.driving_privileges"
},
"un_distinguishing_sign": {
// Distinguishing sign of the issuing country according to ISO/IEC 18013-1:2018,
// Annex F (UN distinguishing sign) (mandatory)
"type": "string",
"defaultValue": "USA"
},
"administrative_number": {
// An audit control number assigned by the issuing authority (optional)
"type": "string",
"mapFrom": "claims.administrative_number"
},
"sex": {
// mDL holder's sex using values as defined in ISO/IEC 5218 (optional)
// 0 = not known, 1 = male, 2 = female, 9 = not applicable
"type": "number",
"mapFrom": "claims.sex"
},
"height": {
// mDL holder's height in centimetres (optional)
"type": "number",
"mapFrom": "claims.height"
},
"weight": {
// mDL holder's weight in kilograms (optional)
"type": "number",
"mapFrom": "claims.weight"
},
"eye_colour": {
// mDL holder's eye colour (optional)
// Valid values: black, blue, brown, dichromatic, grey, green, hazel, maroon, pink, unknown
"type": "string",
"mapFrom": "claims.eye_colour"
},
"hair_colour": {
// mDL holder's hair colour (optional)
// Valid values: bald, black, blond, brown, grey, red, auburn, sandy, white, unknown
"type": "string",
"mapFrom": "claims.hair_colour"
},
"birth_place": {
// Country and municipality or state/province where the mDL holder was born (optional)
"type": "string",
"mapFrom": "claims.birth_place"
},
"resident_address": {
// The place where the mDL holder resides and/or may be contacted (optional)
"type": "string",
"mapFrom": "claims.resident_address"
},
"portrait_capture_date": {
// Date when portrait was taken in full-date format as defined in RFC 3339 (optional)
"type": "date",
"mapFrom": "claims.portrait_capture_date"
},
"age_in_years": {
// The age of the mDL holder (optional)
"type": "number",
"mapFrom": "claims.age_in_years"
},
"age_birth_year": {
// The year when the mDL holder was born (optional)
"type": "number",
"mapFrom": "claims.age_birth_year"
},
"age_over_18": {
// Indication whether the mDL holder is over 18 years old (optional)
"type": "boolean",
"mapFrom": "claims.age_over_18"
},
"age_over_21": {
// Indication whether the mDL holder is over 21 years old (optional)
"type": "boolean",
"mapFrom": "claims.age_over_21"
},
"issuing_jurisdiction": {
// Subdivision code as defined in ISO 3166-2 of the issuing authority (optional)
"type": "string",
"mapFrom": "claims.issuing_jurisdiction"
},
"nationality": {
// Nationality of the mDL holder as an alpha-2 country code as defined in ISO 3166-1 (optional)
"type": "string",
"mapFrom": "claims.nationality"
},
"resident_city": {
// The city where the mDL holder lives (optional)
"type": "string",
"mapFrom": "claims.resident_city"
},
"resident_state": {
// The state/province/district where the mDL holder lives (optional)
"type": "string",
"mapFrom": "claims.resident_state"
},
"resident_postal_code": {
// The postal code of the mDL holder (optional)
"type": "string",
"mapFrom": "claims.resident_postal_code"
},
"resident_country": {
// The country where the mDL holder lives as an alpha-2 code as defined in ISO 3166-1 (optional)
"type": "string",
"mapFrom": "claims.resident_country"
},
"family_name_national_character": {
// Family name of the mDL holder in national characters (optional, deprecated)
// Note: This data element is deprecated and should not be used for new implementations
"type": "string",
"mapFrom": "claims.family_name_national_character"
},
"given_name_national_character": {
// Given name of the mDL holder in national characters (optional, deprecated)
// Note: This data element is deprecated and should not be used for new implementations
"type": "string",
"mapFrom": "claims.given_name_national_character"
},
"signature_usual_mark": {
// Image of the signature or usual mark of the mDL holder (optional)
"type": "binary",
"mapFrom": "claims.signature_usual_mark"
}
}
// ... rest of your credential configuration (credentialType, validityPeriod, etc.)
}
```
mDLs are signed by a Document Signer Certificate (DSC) that chains to an IACA. The
`issuing_country` value must match the country of that IACA, and `issuing_jurisdiction` (if
included) must match the IACA's State or Province. `issuing_country` is an
[ISO 3166-1 alpha-2 code](https://www.iso.org/glossary-for-iso-3166.html) such as `NZ`, and
`issuing_jurisdiction` is an [ISO 3166-2 code](https://www.iso.org/glossary-for-iso-3166.html). If
these values do not match the IACA, issuance fails. Set these values to reflect your own IACA
rather than copying the example values.
ISO/IEC 18013-5:2021 specifies additional age attestation claims (such as `age_over_16`,
`age_over_25`, etc.) that can be included based on jurisdictional requirements. The standard also
requires that mDL credentials have a validity period not exceeding 427 days (approximately 14
months) from the issue date.
### Mandatory and optional data elements for compliant mDL issuance [#mandatory-and-optional-data-elements-for-compliant-mdl-issuance]
According to ISO/IEC 18013-5:2021 (Table 5, Section 7.2), a compliant mobile Driving License must include the following data elements:
**Mandatory data elements**
These data elements must be present in every compliant mDL credential:
* `family_name` — Last name, surname, or primary identifier of the mDL holder
* `given_name` — First name(s) or other name(s) of the mDL holder
* `birth_date` — Date of birth in full-date format (RFC 3339)
* `issue_date` — Date when the mDL was issued
* `expiry_date` — Date when the mDL will expire
* `issuing_country` — Alpha-2 country code (ISO 3166-1) of the issuing authority's country
* `issuing_authority` — Name of the issuing authority
* `document_number` — The number assigned or calculated by the issuing authority
* `portrait` — Portrait image of the mDL holder
* `driving_privileges` — Array containing the driving categories and privileges
* `un_distinguishing_sign` — Distinguishing sign of the issuing country (ISO/IEC 18013-1:2018, Annex F)
**Optional data elements**
These data elements may be included based on jurisdictional requirements and issuer policies:
* `administrative_number` — Audit control number
* `sex` — mDL holder's sex (ISO/IEC 5218 values)
* `height` — Height in centimetres
* `weight` — Weight in kilograms
* `eye_colour` — Eye color (predefined values)
* `hair_colour` — Hair color (predefined values)
* `birth_place` — Country and municipality where the holder was born
* `resident_address` — Residential address
* `portrait_capture_date` — Date when the portrait was taken
* `age_in_years` — Age of the holder
* `age_birth_year` — Birth year of the holder
* `age_over_18`, `age_over_21` — Age attestation booleans (additional age thresholds may be included based on jurisdictional needs)
* `issuing_jurisdiction` — Subdivision code (ISO 3166-2)
* `nationality` — Nationality as alpha-2 country code (ISO 3166-1)
* `resident_city` — City where the holder resides
* `resident_state` — State/province/district where the holder resides
* `resident_postal_code` — Postal code
* `resident_country` — Country of residence as alpha-2 code (ISO 3166-1)
* `signature_usual_mark` — Image of the holder's signature or usual mark
The data elements `family_name_national_character` and `given_name_national_character` are
deprecated in ISO/IEC 18013-5:2021 and should not be used for new implementations.
### Minimal claims mapping object for ISO/IEC 18013-5:2021 compliance [#minimal-claims-mapping-object-for-isoiec-18013-52021-compliance]
The following example shows a minimal `claimMappings` object for your credential configuration that includes only the mandatory data elements required for ISO/IEC 18013-5:2021 compliance. Use this as the foundation of your credential configuration and extend it with optional data elements based on specific jurisdictional requirements:
```json title="Minimal claimMappings object for ISO/IEC 18013-5:2021 compliance"
{
"org.iso.18013.5.1": {
"family_name": {
"type": "string",
"mapFrom": "claims.family_name"
},
"given_name": {
"type": "string",
"mapFrom": "claims.given_name"
},
"birth_date": {
"type": "date",
"mapFrom": "claims.birth_date"
},
"issue_date": {
"type": "date",
"mapFrom": "claims.issue_date"
},
"expiry_date": {
"type": "date",
"mapFrom": "claims.expiry_date"
},
"issuing_country": {
"type": "string",
"defaultValue": "US"
},
"issuing_authority": {
"type": "string",
"defaultValue": "State Department of Motor Vehicles"
},
"document_number": {
"type": "string",
"mapFrom": "claims.document_number"
},
"portrait": {
"type": "binary",
"mapFrom": "claims.portrait"
},
"driving_privileges": {
"type": "array",
"mapFrom": "claims.driving_privileges"
},
"un_distinguishing_sign": {
"type": "string",
"defaultValue": "USA"
}
}
// ... rest of your credential configuration
}
```
**Key points for your credential configuration:**
* **claimMappings**: The object shown above should be included as the `claimMappings` property in your credential configuration. Uses dot notation to map data from the user object (e.g., `claims.family_name`) or default values where appropriate.
* **Credential type**: Set `type` to `org.iso.18013.5.1.mDL` to indicate this is a mobile Driving License compliant with ISO/IEC 18013-5:2021
* **Namespace**: All claims are grouped under the `org.iso.18013.5.1` namespace as required by the standard
* **Issuing country**: Set `issuing_country` to match the country of the IACA at the root of the certificate chain that signs the mDL. A mismatch causes issuance to fail. Refer to [Certificates](/docs/issuance/certificates/overview) for details
* **Validity period**: Must not exceed 427 days as per the standard. Refer to [Credential validity](/docs/issuance/credential-configuration/overview#credential-validity) to learn more about how to set the validity period in your credential configuration
* **Portrait**: The portrait image should be provided as binary data in JPEG or JPEG2000 format
* **Driving privileges**: This is an array that contains objects specifying vehicle categories and associated privileges. Each privilege object should include:
* `vehicle_category_code` — The vehicle category
* `issue_date` — When the privilege was granted
* `expiry_date` — When the privilege expires
* Additional codes or restrictions as applicable
When implementing the `driving_privileges` array, ensure each privilege object conforms to the
structure defined in ISO/IEC 18013-5:2021, Section 7.2.4. The array must contain at least one
valid driving privilege entry.
## ISO/IEC TS 23220-2 namespace and claims [#isoiec-ts-23220-2-namespace-and-claims]
ISO/IEC TS 23220-2:2024 is a Technical Specification that defines reusable namespaces and claims. A second edition is currently under development and is expected to be published soon. If compliance with ISO/IEC TS 23220-2:2024 is desired, you can use the reusable namespace and claims in your credential configuration. Reusing these common namespace and claims helps ensure semantic consistency across different credential types.
The example below shows a `claimMappings` object you can use in your credential configuration to issue ISO/IEC TS 23220-2:2024 compliant credentials. This example includes all the claims defined in the standard, but in practice you would typically select only the claims that are relevant to your use case:
```json title="Example claimMappings object for ISO/IEC TS 23220-2:2024 compliance"
{
"org.iso.23220.1": {
"family_name_unicode": {
// Last name, surname, or primary identifier of the holder (unicode characters)
"type": "string",
"mapFrom": "claims.family_name_unicode"
},
"given_name_unicode": {
// First name(s), other name(s), or secondary identifier of the holder
"type": "string",
"mapFrom": "claims.given_name_unicode"
},
"sex": {
// Holder's sex using values as defined in ISO/IEC 5218 (0=Unknown, 1=Male, 2=Female, 9=Not applicable)
"type": "number",
"mapFrom": "claims.sex"
},
"height": {
// Holder's height in centimetres (uint)
"type": "number",
"mapFrom": "claims.height"
},
"weight": {
// Holder's weight in kilograms (uint)
"type": "number",
"mapFrom": "claims.weight"
},
"birthplace": {
// Country and municipality or state/province where the holder was born
"type": "string",
"mapFrom": "claims.birthplace"
},
"resident_address_unicode": {
// The place where the holder resides and/or may be contacted (street/house number, municipality etc.)
"type": "string",
"mapFrom": "claims.resident_address_unicode"
},
"resident_city_unicode": {
// The city/municipality (or equivalent) where the holder lives
"type": "string",
"mapFrom": "claims.resident_city_unicode"
},
"resident_postal_code": {
// The postal code of the holder
"type": "string",
"mapFrom": "claims.resident_postal_code"
},
"resident_country": {
// The country where the holder lives as a two letter country code
// (alpha-2 code) defined in ISO 3166-1
"type": "string",
"mapFrom": "claims.resident_country"
},
"biometric_template_face": {
// A reproduction of the holder's portrait
"type": "binary",
"mapFrom": "claims.biometric_template_face"
},
"portrait": {
// Portrait data as specified in ISO/IEC 18013-2:2020, Annex D,
// but only JPEG or JPEG2000 shall be supported.
"type": "binary",
"mapFrom": "claims.portrait"
},
"portrait_capture_date": {
// Date when portrait was taken
"type": "date",
"mapFrom": "claims.portrait_capture_date"
},
"fingerprint": {
// A reproduction of the holder's fingerprint data (TBC)
"type": "binary",
"mapFrom": "claims.fingerprint"
},
"nationality": {
// Nationality of the holder as two letter country code (alpha-2 code)
// or three letter code (alpha-3 code) defined in ISO 3166-1b
"type": "string",
"mapFrom": "claims.nationality"
},
"business_name_unicode": {
// Business name of the holder
"type": "string",
"mapFrom": "claims.business_name_unicode"
},
"organization_name_unicode": {
// Name of the legal person
"type": "string",
"mapFrom": "claims.organization_name_unicode"
},
"name_at_birth": {
// The name(s) which holder was born
"type": "string",
"mapFrom": "claims.name_at_birth"
},
"telephone_number": {
// Telephone number of the holder, including country code
// as specified ITU-T E.123 and ITU-T E.164
"type": "string",
"mapFrom": "claims.telephone_number"
},
"email_address": {
// E-mail address of the holder
"type": "string",
"mapFrom": "claims.email_address"
},
"profession": {
// Profession of the holder
"type": "string",
"mapFrom": "claims.profession"
},
"title": {
// Academic title of the holder
"type": "string",
"mapFrom": "claims.title"
},
"age_in_years": {
// The age of the holder
"type": "number",
"mapFrom": "claims.age_in_years"
},
"age_birth_year": {
// The year the holder was born
"type": "number",
"mapFrom": "claims.age_birth_year"
},
"age_over_NN": {
// e.g., age_over_18, age_over_21
"type": "boolean",
"mapFrom": "claims.age_over_NN"
},
"issuing_country": {
// Country code as alpha 2 and alpha 3 code, defined in ISO 3166-1,
// which issued the credential or within which the issuing
// authority is located
"type": "string",
"mapFrom": "claims.issuing_country"
},
"issuing_subdivision": {
// Subdivision code as defined in ISO 3166-2, which issued
// the credential or within which the issuing authority located
"type": "string",
"mapFrom": "claims.issuing_subdivision"
},
"issuing_authority_unicode": {
// Name of issuing authority
"type": "string",
"mapFrom": "claims.issuing_authority_unicode"
},
"issue_date": {
// Date the credential was issued
"type": "date",
"mapFrom": "claims.issue_date"
},
"expiry_date": {
// Date the credential expires
"type": "date",
"mapFrom": "claims.expiry_date"
},
"document_type": {
// The document type
"type": "string",
"mapFrom": "claims.document_type"
},
"document_number": {
// The number assigned or calculated by the issuing authority
"type": "string",
"mapFrom": "claims.document_number"
}
}
// ... rest of your credential configuration (credentialType, validityPeriod, etc.)
}
```
MATTR VII does not currently support the ISO/IEC TS 23220-2:2024 `birthdate`
claim.
# Credential configurations
URL: /docs/issuance/credential-configuration/overview
Credential configurations are templates that define rules and parameters that are used to issue a
specific type of digital credential. They are referenced in
[Credential offers](/docs/issuance/credential-offer/overview) and define rules and parameters that
control the structure and content of the credentials being issued.
Every MATTR VII tenant can include multiple Credential configurations to meet different use cases.
## Key considerations [#key-considerations]
When creating a new credential configuration, an issuer should take the following factors into
account:
* **Credential type**: Used to uniquely identify credentials issued via this configuration.
* **Credential content**: Decide what information will be included in the credential, and how it
will be structured.
* **Credential validity**: Set the appropriate validity period to reflect how long the credential
should be considered accurate and trustworthy.
* **Revocation support**: Determine whether the credential needs to support revocation status
updates.
* **Branding configuration**: Specify how the credential should appear in a digital wallet. While
display conventions are not yet standardized, MATTR issuance and holding capabilities implement a
proprietary format to support branding, clarity, and holder trust.
* **Availability**: Credential configurations must be publicly available to be used in a credential
offer. This is to ensure that wallet applications can always access the configuration when they receive a
credential offer.
## Credential type [#credential-type]
The credential type is used to differentiate between different types of credentials. It is a unique
identifier that specifies the purpose and structure of the credential being issued. For example:
* `UniversityDegreeCredential` : Indicates that the credential being issued is a university degree
certificate.
* `EmployeeIDCredential` : Indicates that the credential being issued is an employee ID card.
* `VaccinationCertificate` : Indicates that the credential being issued is a vaccination record.
Some credential types are standardized and widely recognized, such as:
* `org.iso.18013.5.*.mDL` : Mobile Driving License (mDL) compliant with the ISO/IEC 18013-5:2021 standard.
* `org.iso.23220.photoid.1` : Mobile Photo ID compliant with the ISO/IEC 23220-3:2024 standard.
* `eu.europa.ec.eudi.pid.1` : Mobile Person Identification Data compliant with the EU Digital Identity framework.
* `org.iso.7367.1.mVC` : Mobile Vehicle Certificate compliant with the ISO/IEC 7367 standard.
* `org.micov.1` : eHealth / Mobile International Certificate of Vaccination compliant with the WHO guidelines.
* `org.iso.23220.1.jp.mnc` : Japanese My Number Card compliant with the ISO/IEC 23220-3:2024 standard.
Some MATTR VII issuance logic is based on the set credential type. For example, when set to
`org.iso.18013.5.*.mDL` (where \* is a positive integer), MATTR VII recognizes that this is an
attempt to create an mDL credential configuration and will fail if the validity period is set to 427
days or more to comply with ISO/IEC 18013-5:2021.
*Best Practices*
To comply with the emerging ISO/IEC TS 23220 requirements, credential types must be named using a
reverse domain name that is under the control of the type designer. For example:
* `com.example.credentialtype.1`
This approach is necessary because registration in the ISO/IEC 23220-7 type registry requires proof
of domain ownership.
It is also recommended to include a version identifier (e.g., .1, .2) at the end of the credential
type name. This makes it possible to introduce new versions without breaking compatibility and
ensures it is always clear which version of the type is being referenced.
## Credential content [#credential-content]
The credential content defines the specific information that will be included in the credential,
commonly referred to as *claims*. These claims can include various details about the credential
holder such as their name, email address, and any other relevant data.
### Namespaces [#namespaces]
Namespaces provide a structured way to organize claims within a credential. They act like containers
or “labels” that group related claims together, ensuring that their meaning is unambiguous and
consistent across different contexts. Using namespaces helps avoid confusion when similar claims
appear in different credential types, and it enables reuse of standard definitions across multiple
contexts.
For instance, both a university credential and a driver’s license might include a claim called
`issue_date`, but the meaning differs — in the university context it refers to when the student ID
was issued, while in the driver’s license it refers to when the license was granted. By grouping
these under separate namespaces (`org.example.university.1.issue_date` vs.
`org.example.driverslicense.1.issue_date`), each claim is clearly contextualized, avoiding confusion
while still allowing both credentials to coexist in the same ecosystem.
claimMappings can be defined in two ways:
* **Flat namespace**: All claims are expressed at the same level.
* **Multiple/grouped namespaces**: Claims are grouped into structured objects under different
namespaces.
For example, the ISO/IEC 18013-5 specification for mobile Driving Licenses (mDLs) defines a standard
`org.iso.18013.5.1` namespace that covers a wide set of claims such as `family_name`, `given_name`,
`birth_date`, and `portrait`. This namespace is not exclusive to driver licenses. It can be reused
across other mDoc types, such as a mobile ID (mID) or a residence permit, which also need to
represent personal identity attributes.
Regardless of the approach, the semantics of common claims must remain unchanged. For example,
`first_name` should always refer to the credential holder’s first name. Any additional claims should
be clearly named or placed under an appropriate namespace to prevent ambiguity.
The following examples illustrate both approaches:
```json title="Flat namespace"
{
"claimMappings": {
"org.example.studentid.1": {
"first_name": {
"defaultValue": "Alice",
"type": "string"
},
"last_name": {
"defaultValue": "Miller",
"type": "string"
},
"birth_date": {
"defaultValue": "2001-05-14",
"type": "date"
},
"university_name": {
"defaultValue": "Kakapo University of Technology",
"type": "string"
},
"student_id": {
"defaultValue": "12345678",
"type": "string"
},
"program": {
"defaultValue": "Computer Science",
"type": "string"
},
"enrollment_year": {
"defaultValue": 2020,
"type": "number"
}
}
}
}
```
```json title="Grouped namespaces"
{
"claimMappings": {
"org.example.common.1": {
"first_name": {
"defaultValue": "Alice",
"type": "string"
},
"last_name": {
"defaultValue": "Miller",
"type": "string"
},
"birth_date": {
"defaultValue": "2001-05-14",
"type": "date"
}
},
"org.example.university.1": {
"university_name": {
"defaultValue": "Kakapo University of Technology",
"type": "string"
},
"student_id": {
"defaultValue": "12345678",
"type": "string"
}
},
"org.example.academic.1": {
"program": {
"defaultValue": "Computer Science",
"type": "string"
},
"enrollment_year": {
"defaultValue": 2020,
"type": "number"
}
}
}
}
```
*Best Practices*
* It is recommended to append a version identifier (e.g., .1, .2) to namespaces (similar to
credential types). This allows new versions of a namespace to be introduced without breaking
interoperability and makes it clear which revision a given namespace belongs to.
* Using multiple namespaces may introduce a small binary size overhead, but it provides better
scalability. This is particularly useful when the same namespace is reused across different
credential types. Reusing namespaces ensures consistency in meaning while avoiding duplication.
### Claims mapping [#claims-mapping]
Credential configurations control the structure and content of issued credentials by defining the
mapping of claims from an internal `user` object into the issued credential. The `user` object is
populated during the OID4VCI workflow and contains information about the user that is being issued a
credential. This information can come from various sources:
* In an [Authorization Code flow](/docs/issuance/authorization-code/overview) these can be
retrieved from the configured
[Authentication provider](/docs/issuance/authorization-code/authentication-provider/overview) and/or
[Interaction hook](/docs/issuance/authorization-code/interaction-hook/overview).
* In a [Pre-authorized Code flow](/docs/issuance/pre-authorized-code/overview) these can be
provided directly in the credential offer by the issuer.
* In either flow a compatible [Claims source](/docs/issuance/claims-source/overview) can be
configured to provide additional user attributes.
When using the Authorization Code flow, the claims contributed by your Authentication provider
to the `user` object depend on what the provider returns (for example, via the ID token or
userinfo endpoint). Not all providers support all claims by default. Before defining your claim
mappings, verify that your provider can supply the required claims from this source. Additional
attributes can still be added to the `user` object via an Interaction hook or Claims source. See
[Ensuring claim availability](/docs/issuance/authorization-code/authentication-provider/overview#ensuring-claim-availability)
for guidance on checking your provider's supported claims and scopes.
This is an example of what a `user` object looks like:
```json title="User object example"
{
"authenticationProvider": {
"url": "https://account.example.com",
"subjectId": "145214ad-3635-4aff-b51d-61d69a3c8eee"
},
"claims": {
"given_name": "John",
"family_name": "Doe",
"email": "john.doe@example.com",
"address": {
"formatted": "123FooRd,BarWorld"
}
}
}
```
* `authenticationProvider` (only provided for users in an
[Authorization Code flow](/docs/issuance/authorization-code/overview)) : This object contains
* `url` : References the
[Authentication provider](/docs/issuance/authorization-code/authentication-provider/overview) that is used to
authenticate this user.
* `subjectId` : Indicates the unique identifier of the user with this Authentication provider.
* `claims` : This object is used to hold claims fetched as part of the OID4VCI workflow. The claims
object can contain simple key-value pairs (e.g., `given_name`, `family_name`, `email`) or nested
objects (e.g., `address`).
The actual mapping from the user object into the issued credential is defined in the `claimMapping`
object in the credential configuration. Each item in the object represents a credential claim and
can be mapped in different ways, depending on the use case.
Here is an example of a `claimMapping` object:
```json title="claimMappings object example"
{
"claimMappings": {
"dateOfBirth": {
"mapFrom": "claims.dateOfBirth",
"required": true,
"type": "string"
},
"email": {
"mapFrom": "claims.email",
"required": false,
"defaultValue": "Not available",
"type": "string"
}
}
}
```
* `mapFrom` : This is used to map a claim in the issued credential to a specific attribute in the
claims object. In this example, the `dateOfBirth` claim in the issued credential is mapped
directly from `claims.dateOfBirth` in the user object, and the `email` claim is mapped from
`claims.email`.
* `type` : This defines the data type of the claim. Refer to the API Reference for
[available types](/docs/issuance/credential-configuration/api-reference/mdocs#create-an-mdocs-credential-configuration!path=claimMappings/additionalProperties/additionalProperties\&t=request)
and their usage. In this example, both `dateOfBirth` and `email` are defined as strings.
* `required` : When set to `true`, the claim must be provided during issuance. If it is not provided
and no `defaultValue` is set, issuance will fail. If set to `false` or not present, the claim is
optional and issuance will succeed even if the claim is not provided. In this example,
`dateOfBirth` is a required claim, while `email` is optional.
* `defaultValue` : This is used to set a default value for the claim if it is not provided during
issuance. This is useful for optional claims where a sensible default can be applied. In this
example, `Not available` is set as the default value for the `email` claim.
The following logic is applied when combining these properties:
* Either `mapFrom` or `defaultValue` must be provided for each claim.
* If `mapFrom` is provided and the claim is found in the `user` object, its value will be used.
* If `mapFrom` is provided but the claim is **not** found in the `user` object:
* If `required` is `true` and no `defaultValue` is provided, issuance will fail.
* If `required` is `false` or not present and a `defaultValue` is provided, the `defaultValue`
will be used.
* If `required` is `false` or not present and no `defaultValue` is provided, the claim will be
omitted from the issued credential.
* If only `defaultValue` is provided (without `mapFrom`), the claim will always be set to the
`defaultValue`.
*Best Practices*
* Some credential types require a portrait claim. You must provide the portrait as a binary claim type.
* Maximum supported portrait file size is 500KB. Keep files below 50KB for better performance and user experience.
* When creating credential configurations, consider limitations on the overall size of requests to create credential offers. The total size of the request cannot exceed 500KB in both flows, but the practical impact differs:
* Authorization Code flow: This is usually not an issue, as claims are typically retrieved from the authentication provider or claims source rather than included in the request.
* Pre-authorized Code flow: Claims are supplied in the request itself, so the limit is most relevant when including large claims such as images. If holders present credentials over Bluetooth Low Energy (BLE), keep payloads as small as practical, as large claims can degrade the transfer experience. Reserve larger payloads for remote presentation flows.
#### Avoiding issuance failures due to claim mapping [#avoiding-issuance-failures-due-to-claim-mapping]
In accordance with OID4VCI, an issued credential must contain at least one claim. This ensures that every credential carries meaningful information about the holder.
The `claimMappings` object in a credential configuration defines *how* claims should be mapped into the issued credential. It does not itself guarantee that claims will be present at issuance time. Instead, it specifies where claim values should be sourced from (for example, from the user object or a configured Claims source), and how they should be handled if missing.
Issuance failures related to claims typically occur at issuance time, not when the credential configuration is created. This is because the actual presence of claim values is only determined during the OID4VCI workflow when a credential offer is processed and a credential is issued. For example:
* If a credential configuration defines claim mappings but no matching data is available in the user object or configured Claims source for a particular user, the resulting credential may contain no claims.
* If all mapped claims are optional and no values (or `defaultValues`) resolve during issuance, the credential would effectively be empty.
Because OID4VCI requires at least one claim in an issued credential, such issuance attempts will fail.
For this reason, issuers should carefully design their claim mappings and data sources to ensure that at least one claim will always resolve during issuance:
* Ensure that at least one claim is guaranteed to resolve for all intended users.
* Use `required: true` where appropriate to prevent silent omission of critical claims.
* Provide sensible `defaultValues` where possible to avoid issuance failures due to missing upstream data.
* Validate that your configured Claims source can reliably return data for the user population you intend to issue to.
#### Complex credential structures [#complex-credential-structures]
Credential configurations support complex data structures, including nested objects and arrays. This
enables flexible modeling of real-world data within credential schemas.
The following considerations apply when defining complex structures in the `claimMappings` object:
* Only standard JSON data types are supported.
* When array or object claims are used, selective disclosure works at the array/object level only.
Individual elements cannot be disclosed or hidden independently. This means the holder can either
share the entire array/object or omit it. For example, if multiple bank accounts are listed in an
array, presenting that claim reveals all accounts, which may have privacy implications.
#### Localized display labels [#localized-display-labels]
Each claim in `claimMappings` can optionally include a `display` array that provides localized
labels for the claim. This allows wallet applications to render claim names in the holder's
preferred language, in line with
[OID4VCI §B.2](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-claims-description-for-issu).
Each entry in the array pairs an optional `name` (human-readable label) with an optional `locale`
([BCP47](https://www.rfc-editor.org/rfc/rfc5646.html) language tag). For example:
```json title="Claim with localized display labels"
{
"claimMappings": {
"org.iso.18013.5.1": {
"given_name": {
"mapFrom": "claims.given_name",
"type": "string",
"required": true,
"display": [
{ "name": "Given Name", "locale": "en-US" },
{ "name": "Vorname", "locale": "de-DE" }
]
}
}
}
}
```
When configured, these entries are surfaced in the issuer metadata at
`/.well-known/openid-credential-issuer`, where wallets can read them during the credential offer
flow.
The following constraints apply:
* The `display` array is optional. When present, it must contain at least one entry.
* `locale` values must be unique within a given claim's `display` array. Providing multiple entries with the same `locale` is not allowed and would result in an error.
* `display` is currently only supported for mDoc credential configurations.
## Credential validity [#credential-validity]
Credential validity defines the period during which the credential is considered valid and can be
used for its intended purpose. Invalid credentials would fail verification by any standards
compliant relying party/verifier.
The credential validity can be set using a combination of three properties in the credential
configuration:
* Relative validity:
* [`expiresIn`](/docs/issuance/credential-configuration/api-reference/mdocs#create-an-mdocs-credential-configuration!path=expiresIn\&t=request)
: Determines when will the credential expire in relation to its issuance time and date.
* Absolute validity:
* [`validFrom`](/docs/issuance/credential-configuration/api-reference/mdocs#create-an-mdocs-credential-configuration!path=validFrom\&t=request)
: Specifies an absolute date and time from which the credential is considered valid. For
example, a credential could become valid on a fixed date, regardless of when it is issued.
* [`validUntil`](/docs/issuance/credential-configuration/api-reference/mdocs#create-an-mdocs-credential-configuration!path=validUntil\&t=request)
: Specifies an absolute date and time at which the credential expires. For example, a
credential could be valid until a fixed date, regardless of when it is issued.
Here are two examples of how these properties can be used:
```json title="Setting relative credential validity"
{
"expiresIn": {
"days": 30
}
// Rest of the credential configuration
}
```
This credential will be valid for 30 days from the date and time it is issued.
```json title="Setting absolute credential validity"
{
"validFrom": {
"mapFrom": "claims.validFrom"
},
"validUntil": {
"mapFrom": "claims.validUntil"
}
// Rest of the credential configuration
}
```
In this example, the `validFrom` and `validUntil` claims are mapped from the `claims` object. This allows the
issuer to specify exact dates for when the credential becomes valid and when it expires based on values provided during issuance.
Note that in this example `expiresIn` must also be set, as it is a required
property. Its value will be ignored as both `validFrom` and `validUntil` are
provided.
The following logic is applied when combining these properties:
**Summary of validity decision logic:**
1. If `validFrom` is included in the credential configuration:
* If `validFrom` is also provided in user claims, use the value from user claims.
* Otherwise, use the value from the credential configuration.
2. If `validFrom` is not included, set the credential's valid from date to the date of issuance.
3. If `validUntil` is included in the credential configuration:
* If `validUntil` is also provided in user claims, use the value from user claims.
* Otherwise, use the value from the credential configuration.
4. If `validUntil` is not included, set the credential's valid until date to the date of issuance plus `expiresIn`.
5. Additional validation:
* If `validUntil` is malformed, issuance fails.
* If `validUntil` is earlier than `validFrom`, issuance fails.
* Otherwise, issuance succeeds.
### Validity failures [#validity-failures]
Credential issuance can fail due to validity misconfiguration in the following scenarios:
* Invalid parameters combinations:
* `validUntil` or `validFrom` are in the past.
* `validFrom` is after `validUntil`.
* `expiresIn` is before the `validFrom` date and time (when both are provided).
* Invalid certificates: The validity period of a credential must not exceed the validity period of
the IACA or DSC certificates that form part of its [chain of trust](/docs/concepts/chain-of-trust). If the
`expiresIn`, `validFrom`, or `validUntil` values extend beyond the validity of the active IACA or
any active DSC at the time of issuance, the credential issuance process will fail. For details on
how certificates are selected during mDoc signing, see
[certificate selection](/docs/issuance/certificates/overview#certificate-selection).
* mDL-specific failure:
* When the credential type is set to `org.iso.18013.5.*.mDL` (where \* is a positive integer),
and the `expiresIn` value is set to 427 days or more.
* When the credential type is set to `org.iso.18013.5.*.mDL` (where \* is a positive integer),
and the `validUntil` date is more than 427 days after the issuance date or the `validFrom`
date (if provided).
Certificate validity and credential type checks are enforced only **at the time of credential
issuance**, not when a credential configuration template is created.
## Revocation support [#revocation-support]
MATTR VII allows you to update the status of issued credentials, enabling you to revoke or suspend
credentials when necessary. Verifiers can check the status of a presented credential without
compromising the holder’s privacy. Refer to [revocation](/docs/issuance/revocation/overview) for more
information.
Revocation support is configured using the
[`includeStatus`](/docs/issuance/credential-configuration/api-reference/mdocs#create-an-mdocs-credential-configuration!path=includeStatus\&t=request)
boolean in the credential configuration. When set to `true`, issued credentials include a `status`
object, which refers a [Status list](/docs/issuance/revocation/overview#status-list) where the credential
revocation status is indicated.
## Credential branding [#credential-branding]
Credential configurations can include information regarding how to brand them when they are
displayed in a digital wallet. This is useful for enhancing the user experience and ensuring that
the credential is easily recognizable.
While display conventions are not yet standardized, MATTR holding and issuance capabilities
implement a proprietary format to support branding, clarity, and holder trust. This format includes
the following elements:
* `name` : Displayed on the top part of the credential.
* `description` : Displayed below the name field.
* `backgroundColor` : Applied as the credential background color.
* `watermarkImage` : Displayed as a pattern on top of the credential.
* `issuerLogo` : Displayed on the bottom part of the credential.
* `issuerIcon` : Displayed next to the issuer’s name.
Refer to the
[API Reference](/docs/issuance/credential-configuration/api-reference/mdocs#create-an-mdocs-credential-configuration!path=branding\&t=request)
for specific requirements for each of these elements.
## Availability [#availability]
Once you create a credential configuration, its details are available on your tenant's
`./well-known/openid-credential-issuer` endpoint
(`https://{your_tenant_url}/.well-known/openid-credential-issuer`) so that credentials available
from an issuer can be looked up by wallet applications. If this endpoint is not resolvable, OID4VCI workflows
would fail. This is especially important when configuring a
[Custom domain](/docs/platform-management/custom-domain-overview), which requires creating a redirect from your
custom domain URL to your MATTR VII tenant URL where this resource is available.
# Credential reports
URL: /docs/issuance/credential-reports/guide
Description: Learn how to generate credential reports to track issuance and status change operations across your tenant, broken down by day and credential type.
Credential reports provide a summary of credential lifecycle operations across your tenant. You can
use them to track issuance volumes and status changes (such as revocations) for mDoc and CWT
credentials, broken down by day and credential type.
This gives you visibility into how credentials are being managed over time, without needing to query
individual events.
## Use cases [#use-cases]
Credential reports help you answer questions like:
* **How many credentials were issued this month?** Track daily issuance volumes to understand
adoption trends and usage patterns.
* **How many credentials have been revoked?** Monitor status change operations to stay on top of
credential lifecycle management.
* **What types of credentials are being issued?** Break down issuance by credential type (e.g.
`org.iso.18013.5.1.mDL`) to understand which credential profiles are most active.
* **Are issuance volumes consistent?** Identify unexpected spikes or drops in activity that may
indicate integration issues or changes in demand.
## Report structure [#report-structure]
Each report groups operations by several dimensions so you can analyze trends over time:
* Operation: The type of credential lifecycle operation, such as `issued` or `status_changed`.
* Credential profile: The credential profile, such as `mdoc` or `cwt`.
* Credential type: The specific credential type associated with the operation, if applicable (e.g.
`org.iso.18013.5.1.mDL` or `VerifiedEmployee`).
* Date: The date the operations occurred.
## Generate a credential report [#generate-a-credential-report]
1. Select **Analytics** under **Platform Management** in the sidebar.
2. Use the *Date Range* selector to choose the reporting period. Note that the maximum range for a single report is 31 days, and that the report will include all operations that occurred from the start date at 00:00:00 to the end date at 23:59:59 in the user's timezone.
3. Use the *Credential format* checkboxes to select which credential profiles to include (mDoc, CWT, or both).
4. Click **Generate** to view the results.\
The resulting table will show you the count of operations matching each combination of dimensions (operation, credential profile, credential type) for each date in the selected range.
5. Select **Download CSV** to export the full report data for further analysis, or **Done** to return to the Analytics overview.
Make the following API request to [create a credential report](https://learn.mattr.global/docs/api-reference/platform/analytics/createCredentialReport) for your tenant:
```http title="Request"
POST /v1/events/credential-reports
```
```json title="Request body"
{
"timezoneOffset": "+02:00",
"startDate": "2026-02-01",
"endDate": "2026-02-28",
"profiles": ["cwt", "mdoc"],
"limit": 500,
"cursor": "b2Zmc2V0PTM="
}
```
* `timezoneOffset`: Timezone offset in ISO-8601 format (e.g. `+02:00`, `-05:00`). Determines how
operation timestamps are grouped by date. For example, an operation at `2026-02-25T22:30:00Z`
would be counted towards `2026-02-26` when using `+02:00`, but towards `2026-02-25` when using
`-05:00`.
* `startDate`: Start date for the report (inclusive), interpreted in the specified timezone.
* `endDate`: End date for the report (inclusive), interpreted in the specified timezone.
* `profiles`: Credential profiles to include in the report. Accepted values: `cwt`, `mdoc`.
* `limit` *(optional)*: Maximum number of entries to return. Defaults to `1000` if omitted.
* `cursor` *(optional)*: Pagination cursor. If omitted, results start from the most recent entry.
*Response*
```json title="Response body"
{
"data": [
{
"date": "2026-02-25",
"operation": "issued",
"profile": "mdoc",
"credentialType": "org.iso.18013.5.1.mDL",
"count": 5
},
{
"date": "2026-02-25",
"operation": "status_changed",
"profile": "cwt",
"count": 2
},
{
"date": "2026-02-26",
"operation": "issued",
"profile": "cwt",
"credentialType": "VerifiedEmployee",
"count": 12
}
]
}
```
* `data`: An array of report entries, each representing a count of operations matching the specified
criteria on a given date. Each entry includes the following fields:
* `date`: The date the operations occurred.
* `operation`: The type of credential lifecycle operation: `issued` (a credential was issued) or
`status_changed` (a credential's status was changed, e.g. revoked).
* `profile`: The credential profile: `mdoc` or `cwt`.
* `credentialType`: The credential type associated with the operation, if applicable.
* `count`: The total number of operations matching these criteria on this date.
The `data` array may contain multiple entries for the same date when different operations, profiles,
or credential types were involved. In the example above:
* 5 mDoc mobile driving licences were issued on 25 February 2026.
* 2 CWT credential status changes (e.g. revocations) occurred on the same date.
* 12 CWT `VerifiedEmployee` credentials were issued on 26 February 2026.
# API Reference
URL: /docs/issuance/credential-issuance/api-reference
## Request authorization for access to resources [#request-authorization-for-access-to-resources]
## Exchange authorization code for access token [#exchange-authorization-code-for-access-token]
## Issue a verifiable credential [#issue-a-verifiable-credential]
# End-to-End Encryption
URL: /docs/issuance/credential-issuance/e2e-encryption
End-to-End Encryption is offered as a closed beta preview feature and is not generally available yet. If you are interested in trying this feature, please [contact us](mailto:dev-support@mattr.global).
## Overview [#overview]
End-to-End Encryption (E2E Encryption) provides an additional layer of security for credential issuance workflows where intermediary services are involved between the credential issuer and the wallet application.
In some credential issuance scenarios, a wallet application's backend server acts as a proxy or middleman between the credential issuer (MATTR VII) and individual wallet app instances. In these architectures, the credential response passes through the wallet provider's backend infrastructure before reaching the specific wallet instance on a user's device.
E2E Encryption ensures that credential data and personally identifiable information (PII) remain confidential and unreadable to intermediary backend servers, even as they facilitate the credential delivery process. The credentials are encrypted end-to-end, from the issuer directly to the specific wallet instance, ensuring that only the intended recipient can decrypt and access the credential data.
To understand where End-to-End Encryption fits within the credential issuance process, consider the standard OID4VCI workflow:
1. The wallet obtains an Authorization Code (either after the user authenticates with the configured authentication provider in an Authorization Code flow, or directly from the credential offer in a Pre-authorized Code flow).
2. The wallet makes a request to a Token endpoint to exchange the Authorization Code for a Token.
3. The wallet makes a request to the credential endpoint to issue the credential using the Token.
4. The MATTR VII tenant responds with the issued credential.
With End-to-End Encryption enabled, both the credential request (step 3) and the credential response (step 4) can be encrypted.
Request encryption and response encryption are independent, allowing implementations to encrypt just the request, just the response, or both. When both the request and response are encrypted, this provides complete End-to-End (E2E) Encryption throughout the credential issuance process.
## How it works [#how-it-works]
End-to-End Encryption operates through a policy-based system that can be configured on a per-issuer, per-client (wallet app) basis. This flexible approach allows issuers to set specific encryption requirements for different wallet applications based on their security and privacy needs:
* **Encryption Policies**: Configure encryption requirements for both credential requests and credential responses independently, providing granular control over data protection throughout the issuance workflow.
* **Request Encryption Policy**: Determine whether wallet applications must encrypt their credential requests (Required or Optional), ensuring sensitive information in the request is protected in transit.
* **Response Encryption Policy**: Determine whether credential responses must be encrypted back to the wallet instance (Required or Optional), protecting issued credentials and PII from intermediary visibility.
* **OID4VCI Compliance**: Encryption policies can be declared through the standard `.well-known/openid-credential-issuer` endpoint to comply with OID4VCI-compliant wallet applications. However, per-client policies configured in MATTR VII take precedence over metadata declarations.
* **HPKE Support**: MATTR VII currently supports Hybrid Public Key Encryption (HPKE) for both request and response encryption, providing modern, standards-based cryptographic protection. This release focuses on HPKE-7. Support for additional algorithms, such as HPKE-0, is planned for future updates to broaden interoperability and flexibility.
## Setting encryption policies [#setting-encryption-policies]
Encryption policies are established for trusted wallet applications. Each policy defines the encryption requirements for that specific wallet client's interaction with the issuer:
* **Request Encryption Policy**: Whether the wallet client must encrypt credential requests sent to the MATTR VII tenant:
* **Required**: Wallet must encrypt all credential requests. MATTR VII will reject unencrypted requests from this wallet.
* **Optional**: Wallet may send encrypted or unencrypted requests. MATTR VII will accept both.
* **Response Encryption Policy**: Whether MATTR VII must encrypt credential responses to this wallet:
* **Required**: MATTR VII will encrypt all credential responses to this wallet.
* **Optional**: MATTR VII will encrypt credential responses based on wallet request. If no encryption details are provided, the response will be unencrypted.
## Credential request encryption [#credential-request-encryption]
When a wallet application sends an encrypted credential request, MATTR VII will decrypt it as part of the issuance flow. The system enforces the configured encryption policies and handles various scenarios:
### Encrypting the request [#encrypting-the-request]
To encrypt the request, follow the encryption process defined in [HPKE with JWE](https://www.ietf.org/archive/id/draft-ietf-jose-hpke-encrypt-15.html#name-message-encryption).
The following simplified steps are derived from Section 7.1 of the specification, focusing on Integrated Encryption Mode with HPKE-7 and encoded with JWE Compact Serialization:
1. Retrieve the credential request encryption key from the [well-known credential issuer metadata endpoint](#credential-request-encryption-metadata).
2. Establish the HPKE sender context:
* The recipient public key should be the public key extracted from the credential issuer metadata in the previous step.
* No `info` is required.
3. Construct the `credential_request` as a JSON object:
```jsx title="Credential Request"
{
"format": "mso_mdoc",
"doctype": "org.iso.18013.5.1.mDL2",
"credential_response_encryption": {
"jwk": {
"kid": "yCnfbmYMZcWrKDt_DjNebRCB1vxVoqv4umJ4WK8RYjk",
"kty": "EC",
"use": "enc",
"alg": "HPKE-7",
"crv": "P-256",
"x": "gixQJ0qg4Ag-6HSMaIEDL_zbDhoXavMyKlmdn__AQVE",
"y": "ZxTgRLWaKONCL_GbZKLNPsW9EW6nBsN4AwQGEFAFFbM"
},
"enc": "A256GCM"
},
"proof": {
"proof_type": "jwt",
"jwt": "eyJhbGciOiJFUzI1NiIsImp3ayI6eyJrdHkiOiJFQyIsIngiOiJZY1pHUXV6X0ZkQVFuTGM5MDY1RWJDM29PQ3dZd0lWZ0hSUFFSX0R3aXVNIiwieSI6InVqcDg4ZTBjWEo0SkZjeWRCZEt6clFaazM1X2puOWVjanhaTTdkbFpleU0iLCJjcnYiOiJQLTI1NiJ9fQ.eyJqdGkiOiJiN2NmMjcxZC0yMmFiLTRlN2ItYjM5MS1mOGM0YmM4MmFkM2EiLCJpc3MiOiJtb2JpbGV3YWxsZXQiLCJhdWQiOiJodHRwczovL2VuZy1jeS52aWkuYXUzMDEubWF0dHJsYWJzLmlvIiwiaWF0IjoxNzY1NzQ1NTU5fQ.tyNqDw7rkOmRrOeBEDYdeMqOlKq2VrkcvKLAH_hYCWtKHBwXO631AHBGMCOroh8ZYc2kOFp1THWsEIBYExK6EQ"
}
}
```
3. Construct the `jwe_protect_header` as a JSON object:
```jsx title="JWE Protected Header"
{
"alg": "HPKE-7", // HPKE base mode
"kid": RECIPIENT_PUBLIC_JWK.kid,
};
```
4. Compute `encoded_jwe_protect_header` as `base64url(UTF8(JSON.stringify(jwe_protect_header)))`.
5. Encrypt HPKE with sender context:
* `plaintext` should be the binary encoded `credential_request` created above.
* Additional data (`aad`) should be the `UTF8(encoded_jwe_protect_header)`.
6. Construct the JWE as a JWE Compact serialization format:
```jsx title="JWE Compact Serialization"
{encoded_jwe_protect_header}.{encoded_encrypted_key}.{encoded_iv}.{encoded_ciphertext}.{encoded_tag}
```
* `encoded_encrypted_key` should be the base64Url encoded encapsulated key from the sender context.
* `encoded_ciphertext` should be the base64url encoded encrypted data from step 5.
* `encoded_iv` should be an empty string.
* `encoded_tag` should be an empty string.
7. Share the resulting JWE string as the value of the `credential_request` parameter when making a request to the credential endpoint.
### Credential request encryption metadata [#credential-request-encryption-metadata]
The issuer's encryption capabilities are declared in the `.well-known/openid-credential-issuer` endpoint metadata. This allows wallet applications to discover the issuer's encryption support and requirements.
The `credential_request_encryption` object declares the issuer's ability to receive and decrypt credential requests:
```json title="Credential request encryption metadata"
{
"credential_request_encryption": {
"jwks": {
"keys": [
{
"kty": "EC",
"kid": "issuer-enc-key",
"use": "enc",
"crv": "P-256",
"alg": "HPKE-7",
"x": "YO4epjifD-KWeq1sL2tNmm36BhXnkJ0He-WqMYrp9Fk",
"y": "Hekpm0zfK7C-YccH5iBjcIXgf6YdUvNUac_0At55Okk"
}
]
},
"enc_values_supported": ["A256GCM"],
"encryption_required": false
}
}
```
## Credential response encryption [#credential-response-encryption]
MATTR VII can encrypt credential responses to specific wallet instances using encryption keys provided by the wallet application. This follows the OID4VCI standard for credential response encryption.
### Encryption key provisioning [#encryption-key-provisioning]
When encrypting credential responses, MATTR VII uses the specific encryption key provided by the individual wallet instance claiming the credential, ensuring that only that instance can decrypt and access the credential.
Wallet applications provide encryption details in the credential request through the `credential_response_encryption` parameter when making a request to the credential endpoint:
```json title="Credential response encryption parameter"
{
"credential_response_encryption": {
"jwk": {
"kid": "wallet-instance-key",
"kty": "EC",
"use": "enc",
"crv": "P-256",
"alg": "HPKE-7",
"x": "...",
"y": "..."
},
"enc": "A256GCM"
}
}
```
* `jwks` : Contains the recipient (wallet instance) public key for encryption, this should be a key from the wallet client
* `alg` :
* Must be an [HPKE-7](https://www.ietf.org/archive/id/draft-ietf-jose-hpke-encrypt-15.html#name-hpke-7), which refers to JWE Integrated Encryption with HPKE using DHKEM (P-256, HKDF-SHA256) KEM, HKDF-SHA256 KDF, and AES-256-GCM AEAD.
* Must be a P-256 public key, which is required for HPKE-7.
* `enc` : must be `A256GCM`, required for HPKE-7.
### Decrypting the response [#decrypting-the-response]
The contents of an encrypted response will be encoded with JWE Compact Serialization format, with the media type set to `application/jwt` (instead of `application/json` used for unencrypted credential responses).
To decrypt the response content, follow the decryption process defined in [HPKE with JWE](https://www.ietf.org/archive/id/draft-ietf-jose-hpke-encrypt-15.html#name-message-decryption).
The following simplified steps are derived from Section 7.2 of the specification, focusing on Integrated Encryption Mode with HPKE-7 and encoded with JWE Compact Serialization:
1. Parse the JWE Compact Serialization, splitting the JWE string into its five base64url-encoded components:
```jsx title="JWE Compact Serialization parsing"
{encoded_protected_header}.{encoded_encrypted_key}.{encoded_iv}.{encoded_ciphertext}.{encoded_tag}
```
2. base64url decode and parse `encoded_protected_header` as JSON and validate the header payload. The headers should contain the following key material:
* `alg` should be `HPKE-7`.
* `kid` is optional depending on presence of kid in `credential_response_encryption.jwks`
```jsx title="JWE Protected Header"
{
"alg":"HPKE-7",
"kid":"{wallet-key-identifier}"
}
```
3. Establish the HPKE recipient context:
* The recipient key should be the private key of selected key from `credential_response_encryption.jwk`.
* The encapsulated key should be the base64Url decoded `encoded_encrypted_key` from the JWE.
* No `info` is required.
4. Decrypt HPKE with recipient context:
* The ciphertext should be the base64Url decoded `encoded_ciphertext` from the JWE.
* Additional data (`aad`) should be the `UTF8(encoded_protected_header)`.
* `encoded_protected_header` is the **exact first segment string** from the compact JWE (before the first `.`), and **not** the decoded JSON.
5. Encode decrypted content into text and parse as JSON. The resulting JSON should contain an unencrypted credential response payload.
### Credential response encryption metadata [#credential-response-encryption-metadata]
The issuer's encryption capabilities are declared in the `.well-known/openid-credential-issuer` endpoint metadata. This allows wallet applications to discover the issuer's encryption support and requirements.
The `credential_response_encryption` object declares the issuer's ability to encrypt credential responses:
```json title ="Credential response encryption metadata"
{
"credential_response_encryption": {
"alg_values_supported": ["HPKE-7"],
"enc_values_supported": ["A256GCM"],
"encryption_required": false
}
}
```
Currently the `encryption_required` flag is always set to `false`. Encryption requirements are enforced through the per-client policies configured for each wallet application, which override this default value where applicable.
## Security considerations [#security-considerations]
When implementing End-to-End Encryption for credential issuance, consider the following security aspects:
* **Key Management**: Wallet instances must securely generate and manage their encryption keys. These keys should be protected at the device level and never shared with backend servers.
* **Policy Enforcement**: Encryption policies should be set based on the wallet application's architecture and trust requirements. Wallets that use proxy servers for credential delivery are prime candidates for required encryption policies.
* **Encryption Algorithm**: MATTR VII currently supports the [HPKE-7 JWE algorithm](https://www.ietf.org/archive/id/draft-ietf-jose-hpke-encrypt-15.html#name-hpke-7).
* This algorithm provides integrated encryption with HPKE using:
* DHKEM (P-256, HKDF-SHA256) as the Key Encapsulation Mechanism (KEM)
* HKDF-SHA256 as the Key Derivation Function (KDF)
* AES-256-GCM as the Authenticated Encryption with Associated Data (AEAD)
* This combination offers modern, secure cryptographic protection.
* More algorithms will be supported in future releases.
* **Wallet Identification**: Only identified and approved wallet clients can participate in encrypted issuance flows. Ensure wallet clients are properly registered and configured with the appropriate policies.
Refer to the [OID4VCI security considerations](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-application-layer-encryptio) for more details.
# Credential issuance
URL: /docs/issuance/credential-issuance/overview
## Overview [#overview]
When a user scans a credential offer QR code or follows a credential offer deeplink, it triggers the credential issuance flow. This flow enables wallets to securely claim and receive verifiable credentials from MATTR VII issuer tenants.
## Issuance Flow [#issuance-flow]
The credential issuance flow is based on the [OpenID for Verifiable Credential Issuance (OID4VCI)](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html) specification. MATTR VII supports both the Pre-authorized Code and Authorization Code flows.
### Pre-authorized Code Flow [#pre-authorized-code-flow]
In the Pre-authorized Code flow, the authorization code is embedded directly in the credential offer, allowing immediate credential issuance without additional user authentication:
1. As the user scans the QR code or follows the deeplink, a wallet application registered to handle that URL is invoked.
2. The wallet extracts the issuer metadata endpoint URL from the credential offer and queries it to discover the issuer's configuration, including the [token](/docs/api-reference/platform/credential-issuance/postOauthToken) and [credential](/docs/api-reference/platform/credential-issuance/postOpenIdCredential) endpoint URLs.
3. The wallet uses the authorization code found in the credential offer to make a request to the [token](/docs/api-reference/platform/credential-issuance/postOauthToken) endpoint and exchange the code for an access token.
4. The wallet uses the access token returned by the token endpoint to request credential issuance from the [credential](/docs/api-reference/platform/credential-issuance/postOpenIdCredential) endpoint.
5. The credential endpoint creates the credential based on the credential configuration defined in the credential offer and responds with the signed credential.
### Authorization Code Flow [#authorization-code-flow]
In the Authorization Code flow, the user must authenticate with a configured authentication provider before receiving the authorization code:
1. As the user scans the QR code or follows the deeplink, a wallet application registered to handle that URL is invoked.
2. The wallet extracts the issuer metadata endpoint URL from the credential offer and queries it to discover the issuer's configuration, including the [authorize](/docs/api-reference/platform/credential-issuance/getOauthAuthorize), [token](/docs/api-reference/platform/credential-issuance/postOauthToken), and [credential](/docs/api-reference/platform/credential-issuance/postOpenIdCredential) endpoint URLs.
3. The wallet calls the [authorize](/docs/api-reference/platform/credential-issuance/getOauthAuthorize) endpoint, which redirects the user to the configured authentication provider in a webview. Note that only registered wallets can call this endpoint and initiate the authentication process.
4. After completing authentication, the user is redirected by the authentication provider back to the redirect URI configured when making the request to the authorize endpoint. This redirect URI is a path in the wallet that can extract the authorization code from the response returned by the authentication provider following successful authentication.
5. The wallet uses the authorization code to make a request to the [token](/docs/api-reference/platform/credential-issuance/postOauthToken) endpoint and exchange the code for an access token.
6. The wallet uses the access token returned by the token endpoint to request credential issuance from the [credential](/docs/api-reference/platform/credential-issuance/postOpenIdCredential) endpoint.
7. The credential endpoint creates the credential based on the credential configuration defined in the credential offer and responds with the signed credential.
### Flow Diagram [#flow-diagram]
The following diagram illustrates both the Pre-authorized Code flow and Authorization Code flow:
## Implementation Options [#implementation-options]
### Using MATTR Holding Capabilities [#using-mattr-holding-capabilities]
When using MATTR's holding capabilities, this entire interaction is orchestrated by our [holder SDKs](/docs/holding/sdk-overview). The SDKs handle the complex protocol flows automatically, managing authorization, token exchange, and credential retrieval on behalf of your wallet application.
### Building Your Own Wallet [#building-your-own-wallet]
Customers building their own wallet implementations can still claim credentials from MATTR VII issuer tenants by calling the OID4VCI endpoints directly. This approach gives you full control over the wallet experience while maintaining interoperability with MATTR's issuing infrastructure.
### Encryption [#encryption]
In some credential issuance scenarios, a wallet application's backend server acts as a proxy or middleman between the credential issuer (MATTR VII) and individual wallet app instances. In these architectures, the credential response passes through the wallet provider's backend infrastructure before reaching the specific wallet instance on a user's device.
To ensure the security and privacy of credentials in such scenarios, MATTR VII supports End-to-End Encryption (E2E Encryption) for credential issuance. This means that both the request to issue a credential and the response which includes the issued credentials can be encrypted in a way that only the intended recipient can decrypt and access them, even if they pass through intermediary servers. For more information, see [End-to-End Encryption](/docs/issuance/credential-issuance/e2e-encryption).
# Wallet Attestation
URL: /docs/issuance/credential-issuance/wallet-attestation
## Overview [#overview]
**Issue to the wallets you recognise. Prevent the ones you don't.** Wallet Attestation gives you confidence that your credentials are only issued to trusted, verified wallet applications. It lets a wallet cryptographically prove its authenticity before claiming credentials, so you can keep your credentials out of unknown, malicious, compromised, or non-compliant wallet environments.
As an issuer, you maintain a curated list of approved wallets — anchored by the Wallet Attestation root certificates their providers share with you — that are permitted to claim your credentials. This gives you stronger security, clearer compliance alignment, and control over how your credentials are distributed across ecosystems. It adds no friction for end users: attestation is validated automatically in the background as part of the standard credential retrieval flow, so issuance looks and feels the same for the people using the wallet.
This mechanism is particularly valuable when:
* **Enforcing wallet policies**: Ensuring that only authorized and trusted wallet applications can receive credentials from your issuer.
* **Protecting against unauthorized access**: Preventing credential theft by verifying the wallet's identity before issuance.
* **Supporting platform-based attestation**: Leveraging platform-specific security mechanisms (such as mobile app attestation) while presenting them in a standardized format to the issuer.
The MATTR Pi Holder SDKs support Wallet Attestation automatically when
[SDK Tethering](/docs/holding/sdk-operations/sdk-tethering) is configured. For holder-side
implementation details, see
[Wallet Attestation](/docs/holding/credential-claiming-guides/wallet-attestation) in the Holding
documentation.
### What this means for your team [#what-this-means-for-your-team]
Wallet Attestation touches more than just your integration code:
* **For policy makers**: Define which wallet types can receive specific credentials by setting rules for approved wallet providers and applications. This increases assurance that credentials are only issued to trusted wallets, reducing exposure to unknown, malicious, compromised, or non-compliant wallet environments.
* **For product managers and designers**: Your existing issuance flows and user experiences stay the same. You gain stronger assurance and security by issuing only to recognised, verified wallet applications, with attestation proofs securely shared and onboarded between you and each trusted wallet.
* **For solution architects**: Configure your trusted wallet list and onboard each wallet's root certificate as a trust anchor. Design for secure key and certificate management, high availability, observability, error handling, and continuous monitoring of attestation verification outcomes and operational trust signals.
## Standards and specifications [#standards-and-specifications]
Wallet Attestation in MATTR VII is based on the following standards:
* [OAuth 2.0 Attestation-Based Client Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/) - The core specification for Wallet Attestation
* [RFC 9449: DPoP (Demonstrating Proof-of-Possession)](https://www.rfc-editor.org/rfc/rfc9449.html) - DPoP specification
* DPoP Optimization (Combined Mode) - Based on proposed improvements to the attestation specification
## How it works [#how-it-works]
At a high level, Wallet Attestation establishes trust between you and a wallet application through four steps:
1. **Share and onboard the root certificate**: The wallet app provider creates a Wallet Attestation root CA certificate on their MATTR VII tenant and shares it with you. It is then onboarded as the trust anchor for that wallet application — currently through an out-of-band process with MATTR (see [Setting up Wallet Attestation](#setting-up-wallet-attestation)).
2. **Configure and operate**: You configure your issuance flows to require Wallet Attestation for that wallet. Today this is managed through an out-of-band process with MATTR; APIs for wallet trust configuration are planned for future releases.
3. **Retrieve a credential as usual**: When a user retrieves a credential, the wallet app automatically obtains or refreshes a Wallet Attestation proof — bound to that specific wallet instance — and sends it to you as part of the standard credential retrieval flow.
4. **Verify and issue**: You validate the proof against the onboarded root CA certificate. If the proof is valid and the wallet is approved, the credential is issued; otherwise an error is returned to the wallet.
The rest of this section explains the underlying trust model and protocol in detail.
Wallet Attestation operates through a certificate-based trust chain between three parties:
1. **Client Attester (Wallet Backend)**: Manages certificates and issues attestation JWTs to trusted wallet instances.
2. **Client Instance (Wallet Application)**: The individual wallet app on a user's device that requests and claims credentials, holds a key pair, and shares its public key so that MATTR VII can validate proof of possession.
3. **MATTR VII (Credential Issuer)**: Validates the attestation and issues credentials only to trusted wallet instances. If validation fails, MATTR VII does not issue a credential and returns an error to the wallet.
The following diagram illustrates how the three participants interact across the different attestation modes:
The Wallet Attestation flow begins when a wallet instance attests its own identity to its backend—typically by presenting key information or a unique instance identifier. If the backend is satisfied with this attestation, it issues an attestation JWT that cryptographically verifies the wallet's authenticity. The wallet then presents this attestation, along with proof that it controls the associated cryptographic keys, to MATTR VII. MATTR VII validates the entire chain of trust—from the attestation certificate back to a configured root certificate—before allowing credential issuance to proceed.
The MATTR Pi Holder SDK currently only supports **Combined Mode** (with DPoP). If your issuer
tenant is configured to accept credentials from MATTR Pi Holder SDK wallets, ensure that Combined
Mode is enabled.
### Understanding the flow [#understanding-the-flow]
The attestation process involves three stages:
**Attestation issuance**
The wallet backend creates an Attestation JWT that includes a certificate chain in its header. This chain must trace back to one of the root certificates that MATTR VII has been configured to trust for this wallet application. The wallet backend signs this JWT with the private key corresponding to the first certificate in the chain and returns it to the wallet instance.
**Token request with proof of possession**
The wallet instance must prove it controls the private key associated with the public key embedded in the Attestation JWT. How this proof is provided depends on which mode is used:
* **Standard Mode**: The wallet generates a separate Attestation PoP (Proof of Possession) signed with its instance key and sends both the Attestation JWT and PoP to MATTR VII. This returns a standard Bearer access token.
* **Combined Mode** (recommended): When using DPoP for token binding, the wallet generates a single DPoP proof that serves as both the attestation proof and the token binding. This reduces complexity by eliminating the need for a separate Attestation PoP. MATTR VII returns a DPoP-bound access token (and optionally a refresh token).
* **Standard Mode with DPoP**: The wallet provides both an Attestation PoP and a DPoP proof, resulting in a DPoP-bound access token with additional PoP binding for refresh tokens.
MATTR VII validates the entire attestation—verifying the certificate chain, checking signatures, and confirming proof of possession—before issuing the access token.
**Credential issuance**
The wallet uses the validated access token to request credentials from MATTR VII's credential endpoint. Only after successful attestation validation does MATTR VII issue the credential.
### Certificate trust chain [#certificate-trust-chain]
The trust model relies on certificates:
1. **Root Certificates**: The wallet backend establishes root certificates that MATTR VII must trust. Currently these root certificates are shared with MATTR through an out-of-band configuration process.
2. **Certificate Chain**: When the wallet backend creates an Attestation JWT, it includes a certificate chain of end-entity (and possibly intermediate certificates) in the `x5c` header. This chain must verify back to one of the configured root certificates.
3. **Signature Verification**: MATTR VII validates that the Attestation JWT is signed by the private key corresponding to the first certificate included in the `x5c` certificate chain, and that the chain verifies to a configured root certificate.
## Setting up Wallet Attestation [#setting-up-wallet-attestation]
To enable Wallet Attestation for your issuer tenant, you need to:
1. **Configure trusted wallet applications**: Configure which wallet applications are trusted by your tenant. This includes configuring:
* The wallet's client ID
* Root certificates that the wallet backend uses
* Whether DPoP is required for this wallet
2. **Share root certificates**: Provide the root certificates from your wallet backend's certificate infrastructure to MATTR.
3. **Configure attestation requirements**: Determine whether Wallet Attestation is required or optional for each wallet application.
Currently, Wallet Attestation configuration is managed through out-of-band processes. APIs for wallet trust configuration are planned for future releases.
## Attestation proofs [#attestation-proofs]
When requesting tokens from MATTR VII, the wallet must include attestation proofs in the request headers. The proofs consist of:
* **Attestation JWT**: A JWT issued by the wallet backend that attests to the wallet instance's identity and key possession.
* **Attestation DPoP**: A proof that the wallet instance possesses the private key corresponding to the public key in the Attestation JWT.
Three modes of Wallet Attestation are available, based on what the wallet provides:
| Mode | What the wallet sends | When to use |
| --------------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| **Standard Mode** | `Attestation JWT` + `Attestation PoP` | When the wallet does not use DPoP |
| **Combined Mode** | `Attestation JWT` + `DPoP` | When the wallet uses DPoP for token binding. The DPoP proof serves as both the attestation proof and the token binding proof. |
| **Standard Mode with DPoP** | `Attestation JWT` + `Attestation PoP` + `DPoP` | When the wallet uses DPoP for token binding but provides separate attestation and DPoP proofs. |
**Combined Mode** is the recommended approach when using DPoP, as it reduces the number of proofs the wallet must generate and include in requests.
### Attestation JWT [#attestation-jwt]
The Attestation JWT is issued by the wallet backend (Client Attester) and attests that a specific wallet instance possesses a particular cryptographic key.
#### Structure [#structure]
```json title="Header"
{
"alg": "ES256",
"typ": "oauth-client-attestation+jwt",
"x5c": [
"MIIC3zCCAcWgAwIBAgIUJ9R0z5r3e7...base64-cert-leaf...",
"MIIDdzCCAl+gAwIBAgIUFp93k2m9a8...base64-intermediate..."
]
}
```
* `x5c`: Certificate chain that must verify back to a configured root certificate for the requested `client_id`.
```json title="Payload"
{
"sub": "example-wallet-client-id",
"iat": 1300814780,
"exp": 1300819380,
"client_instance_id": "unique-instance-identifier",
"cnf": {
"jwk": {
"kty": "EC",
"use": "sig",
"crv": "P-256",
"x": "18wHLeIgW9wVN6VD1Txgpqy2LszYkMf6J8njVAibvhM",
"y": "-V4dS4UaLMgP_4fY4j8ir7cl1TXlFdAgcx55o7TkcSA"
}
}
}
```
* `sub`: The wallet client ID
* `iat`, `exp`: Standard JWT time claims for issued at and expiration
* `client_instance_id`: Optional unique identifier for a specific wallet instance
* `cnf.jwk`: The public key of the Client Instance Key Pair. Used to verify the signature of DPoP proofs.
#### How MATTR VII validates it [#how-mattr-vii-validates-it]
MATTR VII performs the following validations on the Attestation JWT:
1. **Signature verification**: The JWT must be signed by the key from the first certificate in the `x5c` array
2. **Certificate chain validation**: The certificate chain in `x5c` must verify up to one of the root certificates configured for this wallet
3. **Time validation**: Standard JWT time claim validation (`iat`, `exp`)
4. **Client matching**: The `sub` claim must match the client ID making the request
### Attestation PoP [#attestation-pop]
The Attestation PoP (Proof of Possession) proves that the wallet instance actually possesses the private key corresponding to the public key in the Attestation JWT's `cnf` claim.
#### Structure [#structure-1]
```json title="Header"
{
"alg": "ES256",
"typ": "oauth-client-attestation-pop+jwt"
}
```
```json title="Payload"
{
"aud": "https://your-tenant.vii.mattr.global",
"iat": 1300815780,
"jti": "d25d00ab-552b-46fc-ae19-98f440f25064"
}
```
* `aud`: The MATTR VII tenant URL
* `jti`: Unique identifier for this proof (prevents replay attacks)
* `iat`: Time issued
#### How MATTR VII validates it [#how-mattr-vii-validates-it-1]
1. **Signature verification**: The PoP must be signed with the private key corresponding to the public key in the Attestation JWT's `cnf.jwk`
2. **Time validation**: Standard JWT time claim validation
3. **Replay detection**: The `jti` is checked to ensure this PoP has not been used before
Attestation PoP proofs cannot be reused. Each request must include a fresh Attestation PoP with a unique `jti`.
### Demonstrating Proof-of-Possession (DPoP) [#demonstrating-proof-of-possession-dpop]
When a wallet uses [DPoP (Demonstrating Proof-of-Possession)](https://www.rfc-editor.org/rfc/rfc9449) for token binding, it can leverage **Combined Mode** to simplify attestation. In this mode:
* The wallet generates its DPoP proof using the same key pair referenced in the Attestation JWT's `cnf.jwk`
* The DPoP proof serves dual purposes:
* Proving possession of the attestation key
* Binding tokens to the wallet
* No separate Attestation PoP is needed, reducing complexity
#### Structure [#structure-2]
```json title="Header"
{
"alg": "ES256",
"typ": "dpop+jwt",
"jwk": {
"kty": "EC",
"x": "YcZGQuz_FdAQnLc9065EbC3oOCwYwIVgHRPQR_DwiuM",
"y": "ujp88e0cXJ4JFcydBdKzrQZk35_jn9ecjxZM7dlZeyM",
"crv": "P-256"
}
}
```
```json title="Payload"
{
"htu": "https://your-tenant.vii.mattr.global/v1/oauth/token",
"htm": "POST",
"jti": "4441dede-e4a8-420a-95ee-2f6716bb384d",
"iat": 1764210264,
"ath": "uU0nuZNNPgilLlLX2n2r-sSE7-N6U4DukIj3rOLvzek",
"htcd": "sha-256=:47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=:"
}
```
* `htu`: The HTTP URL of the token endpoint being called
* `htm`: The HTTP method (e.g., `POST`)
* `jti`: Unique identifier for this proof (prevents replay attacks)
* `iat`: Time issued
* `ath`: Optional base64url-encoded SHA-256 hash of the `access_token`. Required when using the access token with resource servers; optional for initial token requests.
* `htcd`: Optional base64-encoded SHA-256 hash (content digest) of the HTTP request payload used to validate integrity.
#### How MATTR VII validates it in Combined Mode [#how-mattr-vii-validates-it-in-combined-mode]
When using Combined Mode, MATTR VII:
1. Validates the Attestation JWT as described above
2. Validates that the public key in the DPoP proof's `jwk` header matches the public key in the Attestation JWT's `cnf.jwk`
3. Validates the DPoP proof signature
4. Performs standard DPoP validations (URL, method, time, replay detection and optionally request body integrity)
{/* To be added when CRUD endpoints are available for wallet trust configuration:
#### Enforcing DPoP
If your wallet application is configured to require DPoP, the wallet must include DPoP proofs in its requests:
- **Attestation JWT + Attestation PoP** (without DPoP): Returns an error (DPoP is required but not provided)
- **Attestation JWT + DPoP**: Accepted (Combined Mode - DPoP serves as both attestation proof and token binding)
- **Attestation JWT + DPoP + Attestation PoP**: Accepted (Standard Mode with DPoP - uses separate attestation and DPoP proofs)
*/}
## Making token requests with Wallet Attestation [#making-token-requests-with-wallet-attestation]
Wallets configured to require Wallet Attestation must include the attestation proofs as HTTP headers when calling the [Token](/docs/issuance/credential-issuance/api-reference#exchange-authorization-code-for-access-token) endpoint.
### Request headers [#request-headers]
Depending on the authentication mode:
```http title="Standard Mode (no DPoP)"
POST /v1/oauth/token HTTP/1.1
Host: your-tenant.vii.mattr.global
Content-Type: application/x-www-form-urlencoded
OAuth-Client-Attestation: eyJhbGciOiJFUzI1NiIsInR5cCI6Im9hdXRoLWNsaWVudC1hdHRlc3RhdGlvbitqd3QiLCJ4NWMiOlsuLi5dfQ...
OAuth-Client-Attestation-PoP: eyJhbGciOiJFUzI1NiIsInR5cCI6Im9hdXRoLWNsaWVudC1hdHRlc3RhdGlvbi1wb3Arand0In0...
grant_type=authorization_code&code=...
```
```http title="Combined Mode (with DPoP)"
POST /v1/oauth/token HTTP/1.1
Host: your-tenant.vii.mattr.global
Content-Type: application/x-www-form-urlencoded
OAuth-Client-Attestation: eyJhbGciOiJFUzI1NiIsInR5cCI6Im9hdXRoLWNsaWVudC1hdHRlc3RhdGlvbitqd3QiLCJ4NWMiOlsuLi5dfQ...
DPoP: eyJhbGciOiJFUzI1NiIsInR5cCI6ImRwb3Arand0IiwiandrIjp7Imt0eSI6IkVDIiwieCI6Ii4uLiIsInkiOiIuLi4ifX0...
grant_type=authorization_code&code=...
```
### Response [#response]
If the attestation is valid, MATTR VII returns an access token:
```json title="Successful response"
{
"access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImF0K2p3dCJ9...",
"token_type": "DPoP",
"expires_in": 14400
}
```
If attestation is invalid or missing when required, an error is returned:
```json title="Error response"
{
"error": "invalid_client",
"error_description": "Client attestation validation failed"
}
```
## Client instance identification [#client-instance-identification]
The `client_instance_id` claim in the Attestation JWT enables wallet applications to include a unique identifier for the specific wallet instance requesting tokens. This identifier is then included in the issued JWT access token.
### Why use client instance identification [#why-use-client-instance-identification]
By including a unique `client_instance_id` in the Attestation JWT, the wallet can:
* **Enable instance tracking**: Maintain a correlation between specific wallet instances and their issued credentials
* **Enable instance-specific credential data**: MATTR VII can use the `client_instance_id` when querying configured claims sources, allowing issuers to retrieve instance-specific data from existing data sources for more refined and personalized issuance journeys
This mechanism is particularly valuable in environments where:
* Multiple wallet instances operate simultaneously (e.g., on different devices).
* Additional verification of the token issuance flow is required.
* Issuers need to customize credential content based on the specific device or application instance receiving the credential.
### How it works [#how-it-works-1]
The `client_instance_id` flow operates as follows:
1. **Unique identifier generated**: A unique identifier for a specific wallet instance is generated (e.g., a UUID or device-specific identifier)
2. **Include in Attestation JWT**: The wallet backend includes this identifier in the `client_instance_id` claim when creating the Attestation JWT
3. **MATTR VII extracts and uses the identifier**: When processing the token request, MATTR VII:
* Extracts the `client_instance_id` from the Attestation JWT
* Includes it in the issued JWT access token
* Can use it when querying configured claims sources to retrieve instance-specific credential data
By making the `client_instance_id` available during credential issuance, MATTR VII enables issuers to tailor credential content based on the specific wallet instance. For example, claims sources can return different data sets, permissions, or attributes based on which device or application instance is receiving the credential, supporting use cases like device-specific entitlements or instance-aware access control.
The following diagram shows how the instance identifier flows through the attestation and token issuance process:
**How the identifier flows:**
1. **Generation**: The wallet instance creates or retrieves a unique identifier for itself (typically a UUID or device-specific identifier).
2. **Embedding in attestation**: When requesting an Attestation JWT from the wallet backend, the instance provides this identifier. The wallet backend embeds it in the `client_instance_id` claim of the Attestation JWT.
3. **Extraction and propagation**: When the wallet requests an access token from MATTR VII, MATTR extracts the `client_instance_id` from the Attestation JWT and includes it in the issued JWT access token.
4. **Use during issuance**: When the wallet subsequently requests credentials, MATTR VII can use the `client_instance_id` from the access token to query claims sources with instance-specific parameters, enabling personalized credential data based on the specific device or application instance.
### Technical details [#technical-details]
The `client_instance_id` claim is optional in the Attestation JWT. When present:
* The **Attestation JWT payload** includes the following claims:
* `sub`: The subject of the Attestation JWT (for example, the wallet client application).
* `exp`: Expiration time of the JWT.
* `client_instance_id`: A unique identifier for the specific wallet instance (for example, a UUID).
* `cnf.jwk`: The public key material that the wallet proves possession of when requesting tokens.
```json title="Attestation JWT payload"
{
"sub": "example-wallet-client-id",
"client_instance_id": "550e8400-e29b-41d4-a716-446655440000",
"exp": 1300819380,
"iat": 1300814780,
"cnf": {
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "base64url-encoded-x-coordinate",
"y": "base64url-encoded-y-coordinate",
"kid": "wallet-key-id"
}
}
}
```
* And the **JWT access token** will contain the same value:
```json title="JWT Access Token payload"
{
"sub": "user-subject-identifier",
"client_id": "example-wallet-client-id",
"client_instance_id": "550e8400-e29b-41d4-a716-446655440000",
"exp": 1300819380,
"iat": 1300814780
}
```
* **JWT access tokens only**: The `client_instance_id` is included only in JWT-format access tokens. JWT access tokens are issued when credential refresh (refresh tokens) is enabled for the tenant; if your tenant uses opaque access tokens (no credential refresh), this claim will not be present.
### Requirements and scope [#requirements-and-scope]
Client instance identification is available under the following conditions:
* This feature applies only to the [Authorization Code flow](/docs/issuance/authorization-code/overview) and is **not supported** for the [Pre-authorized Code](/docs/issuance/pre-authorized-code/overview) flow.
* **JWT access tokens only**: The `client_instance_id` is included only in JWT-format access tokens (when credential refresh is enabled for the tenant), not in opaque tokens.
* **Wallet Attestation enabled**: The wallet must be configured for Wallet Attestation with the appropriate feature flags and configuration.
### Using client instance ID with claims sources [#using-client-instance-id-with-claims-sources]
When the `client_instance_id` is provided through Wallet Attestation, MATTR VII makes it available for use in [Claims source](/docs/issuance/claims-source/overview) requests during credential issuance. This enables issuers to retrieve instance-specific data from external systems based on which specific wallet instance is claiming the credential and supports several use cases:
* **Device-specific entitlements**: Return different credential claims or permissions based on which device is receiving the credential
* **Instance-aware data filtering**: Filter or customize credential data based on the specific wallet instance
* **Audit and tracking**: Track which wallet instances have claimed credentials for compliance or security monitoring
* **Multi-device management**: Manage credentials across multiple devices belonging to the same user, with instance-specific attributes
During the credential endpoint request, MATTR VII queries configured claims sources to retrieve additional credential data. When Wallet Attestation is in use and a `client_instance_id` is present in the access token, MATTR VII includes this information in the data available for request parameter mapping:
```json title="Data available for claims source request mapping"
{
"authenticationProvider": {
"url": "https://auth-provider.example.com",
"subject": "user-123"
},
"claims": {
// ... user persisted claims
},
"credentialConfiguration": {
"id": "credential-config-id",
"type": "VerifiableCredential",
"profile": "mso_mdoc"
},
"issuanceProtocol": "openid4vci", // [!code focus]
"wallet": { // [!code focus]
"id": "wallet-client-id", // [!code focus]
"instanceId": "550e8400-e29b-41d4-a716-446655440000" // [!code focus]
}, // [!code focus]
"client": {
"instanceId": "550e8400-e29b-41d4-a716-446655440000"
}
}
```
The `client` object is retained for backwards compatibility but is being phased out. New claims source configurations should map from the `wallet` object (`wallet.id`, `wallet.instanceId`) instead of `client.instanceId`.
When [creating](/docs/issuance/claims-source/api-reference#configure-a-claims-source) or configuring a claims source, you can map the `wallet.instanceId` to request parameters that will be sent to your claims source server:
```json title="Claims source configuration example"
{
"url": "https://claims.example.com/api/credentials",
"requestParameters": {
"accountType": {
"mapFrom": "claims.accountType"
},
"sub": {
"mapFrom": "authenticationProvider.subject"
},
"deviceId": {
"mapFrom": "wallet.instanceId"
}
}
}
```
With this configuration, when MATTR VII queries the claims source during credential issuance, it will send a request like:
```http
GET https://claims.example.com/api/credentials?accountType=premium&sub=user-123&deviceId=550e8400-e29b-41d4-a716-446655440000
```
The `wallet.instanceId` is only available when Wallet Attestation is enabled and the issued access token is a JWT that includes `client_instance_id`. It will not be available when opaque access tokens are used. If the `wallet.instanceId` is not available (for example, when Wallet Attestation is not in use or the access token does not include `client_instance_id`), the mapped parameter will be omitted from the claims source request. Design your claims source endpoints to handle requests both with and without the instance ID parameter, unless you exclusively use Wallet Attestation for all issuance flows.
## Refresh token binding [#refresh-token-binding]
When issuing refresh tokens (in conjunction with [Credential Refresh](/docs/issuance/refresh/overview)), MATTR VII binds the refresh token to the Wallet Attestation:
* **Combined Mode**: Refresh token binding is handled through DPoP binding (as DPoP serves as the attestation proof)
* **Standard Mode**: Refresh token is bound to the public key in the Attestation JWT's `cnf.jwk`
When the wallet uses the refresh token to obtain a new access token:
* The wallet must provide the same attestation proofs (Attestation JWT + DPoP)
* The public key in the new Attestation JWT must match the key from the original token request
* If the keys don't match, the refresh request will be rejected
This ensures that only the original wallet instance can use a refresh token, even if the refresh token itself is compromised.
## Implementation considerations [#implementation-considerations]
### Certificate management [#certificate-management]
* Root certificates should be protected and rotated according to your organization's security policies
* Multiple root certificates can be configured to support certificate rotation without service interruption
* Expired or compromised certificates should be removed from the trusted configuration immediately
### Key management [#key-management]
* Client Instance Key Pairs should be generated and stored securely on the wallet device
* Private keys should never leave the device or be transmitted to any server
* Consider using hardware-backed keystores or secure enclaves when available
### Replay protection [#replay-protection]
* Always include a unique `jti` in Attestation PoP and DPoP proofs
* MATTR VII maintains replay detection to prevent reuse of proofs
* Attestation JWTs can be reused within their validity period, but Attestation PoP proofs cannot
### Time synchronization [#time-synchronization]
* Ensure wallet devices have accurate time synchronization
* Clock skew can cause valid attestations to be rejected
* Use reasonable validity windows in `iat` and `exp` claims to account for minor time differences
### Client Instance Validation [#client-instance-validation]
When implementing client instance identification:
* **Unique identifiers**: Use truly unique identifiers for each wallet instance to prevent collisions
* **Identifier format**: While any string value is supported, UUIDs or similar globally unique identifiers are recommended
* **Security implications**: The `client_instance_id` is not encrypted in the JWT access token, so avoid including sensitive information in the identifier value
# API Reference
URL: /docs/issuance/credential-offer/api-reference
## Create an Authorization Code flow Credential Offer [#create-an-authorization-code-flow-credential-offer]
## Create a Pre-Authorized Code flow Credential Offer [#create-a-pre-authorized-code-flow-credential-offer]
## Delete a Pre-Authorized Code flow Credential Offer [#delete-a-pre-authorized-code-flow-credential-offer]
# How to create an OID4VCI credential offer
URL: /docs/issuance/credential-offer/guide
To issue a credential via the [OID4VCI](/docs/issuance/oid4vci-overview) workflow you must create a
[Credential offer](/docs/issuance/credential-offer/overview). This offer specifies the
[Credential configurations](/docs/issuance/credential-configuration/overview) that will be used to
issue the credential, as well as additional parameters to support the issuance workflow.
Once the offer is created, it must be shared with the credential's intended holder so that they can
claim the credential.
The process is similar for both the
[Authorization Code flow](/docs/issuance/authorization-code/overview) and the
[Pre-authorized Code flow](/docs/issuance/pre-authorized-code/overview). The main difference is
the type of credential offer you need to create, depending on which workflow you are using.
## Prerequisites [#prerequisites]
* The `id` identifier of one or more
[Credential configurations](/docs/issuance/credential-configuration/overview) you wish to include
in this offer. This is obtained when you
[create a Credential configuration](/docs/issuance/credential-configuration/overview).
* DIDs (Only required when [sharing the offer as a DID message](#send-an-offer-uri)):
* Issuer DID: This is a [`did:web`](/docs/concepts/dids#didweb) that identifies the issuer who
attests the claims in the credential are accurate.
* Subject DID: This is a [`did:key`](/docs/concepts/dids#didkey) that identifies the intended holder
of the credential. This DID is usually retrieved from the intended holder's digital wallet.
* In production environments you must have a secure way to obtain the holder's digital
wallet DID:
* Use DID Auth for any new interactions.
* Ask the user to share their wallet DID.
* Request an existing credential as part of a verification workflow, and extract the DID
from that interaction.
## Overview [#overview]
The OID4VCI credential offer lifecycle comprises the following steps:
1. [Generate an offer URI](#generate-an-offer-uri).
2. [Send an offer URI](#send-an-offer-uri).
3. [Accept a Credential offer](#accept-a-credential-offer).
### Generate an offer URI [#generate-an-offer-uri]
Generate the offer URI by making one of the following requests based on the issuance flow you are
using:
1. In the navigation panel on the left-hand side, expand the **Credential Issuance** menu.
2. Select **Credential offer**.
3. Select the **Select** button.
4. Check the checkbox next to the credential configuration you wish to include in the credential offer.
5. Select the **Apply** button.
6. Select the **Generate** button.
7. Download the generated QR code by selecting the **Download** button.
Make a request of the following structure to
[generate a new credential offer](/docs/issuance/authorization-code/api-reference#create-credential-offer)
for an [OID4VCI Authorization Code flow](/docs/issuance/authorization-code/overview):
```http title="Request"
POST /v1/openid/offers
```
```json title="Request body"
{
"credentials": ["20d6bbe6-a978-447c-b5bd-f33b6dca19e2"],
"request_parameters": {
"login_hint": "user@example.com",
"prompt": "login"
}
}
```
* `credentials` : This array includes a list of identifiers for credential configurations that will
be included in the credential offer. These identifiers are the `id` elements returned in the
response when you [create a Credential configuration](/docs/issuance/credential-configuration/overview).
To issue multiple credential formats of the same credential in a single flow, include all the
required credential configuration id elements in the request payload. For example, you could
issue a CWT and mDocs credentials using the same data in a single user journey.
* `request_parameters` (*optional*): Specifies a list of additional request parameters that are
included in the credential offer and can be used by the wallet as part of the authentication
workflow:
* `login_hint` : Login hints are included in the authentication flow the holder is redirected to
after accepting the credential offer. For example, you can include the user's e-mail so that
it is already populated in the login screen.
* `prompt` : Prompts are sent to the
[Authentication provider](/docs/issuance/authorization-code/authentication-provider/overview) to control the
authentication flow. For example, using `login` would always require the user to authenticate,
even if they had already completed login on the same device.
*Response*
```json title="Response body"
{
"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%22request_parameters%22%3A%7B%22login_hint%22%3A%22user%40example.com%22%2C%22prompt%22%3A%22login%22%7D%7D"
}
```
* `uri` : This is the URI that should be used by the intended holder to claim the credential. For an
Authorization Code flow credential offers this URI includes the parameters required to redirect
the user to the configured Authentication provider to complete authentication before issuing the
credential.
When a multi-format credential offer is created, this `uri` is used to issue
all the credential formats in a single workflow.
The Pre-authorized Code flow is only supported for
[mDocs](/docs/concepts/mdocs).
Creating pre-authorized code flow credential offers in the MATTR Portal is for testing purposes only. The populated user ID is generated by the MATTR Portal and cannot be changed to another user later.
1. In the navigation panel on the left-hand side, expand the **Credential Issuance** menu.
2. Select **Credential offer**.
3. Use the *Workflow* radio button to select **Pre-authorized code flow**.
4. Select the **Select** button.
5. Check the checkbox next to the credential configuration you created in the previous step.
6. Select the **Apply** button.
7. Use the *Claims* panel to add any claims you wish to include in the issued credential.\
These claims must match the mapping defined in the [Credential configuration](/docs/issuance/credential-configuration/overview#claims-mapping).
8. Use the *Claims to persist* textbox to specify any claims you wish to persist in the MATTR VII database.\
By default no claims are persisted.
9. Use the *Transaction code mode* dropdown to select the desired mode for the transaction code.
10. Use the *Offer valid for* textbox to specify how long the offer will be valid for.\
By default, the offer is valid for 5 minutes, and the maximum allowed duration is 10 minutes.
11. Select the **Generate** button.
12. If a transaction code was configured, copy it from the screen.\
In production deployments you will need to provide it to the user through a different channel
(e.g., email, SMS).
13. Download the displayed QR code.\
This QR code will be used by the holder to claim the credential.
Make a request of the following structure to
[generate a new credential offer](/docs/issuance/pre-authorized-code/api-reference#create-credential-offer)
for an [OID4VCI Pre-authorized Code flow](/docs/issuance/pre-authorized-code/overview):
The total size of the request cannot exceed 500KB, including any claims. This is mostly relevant
when including large claims such as images.
When deciding how large your claims can be, consider how the credential will be presented. If
holders present the credential over Bluetooth Low Energy (BLE), large payloads such as
high-resolution images can degrade the transfer experience or fail on some devices. We recommend
keeping payloads as small as practical for BLE, and reserving larger payloads for remote
presentation flows. See the [credential configuration best
practices](/docs/issuance/credential-configuration/overview#credential-content) for more guidance on
claim sizes.
```http title="Request"
POST /v1/openid/offers/pre-authorized
```
```json title="Request body"
{
"credentials": ["707e920a-f342-443b-ae24-6946b7b5033e"],
"userId": "b7e2c8f4-1a2b-4c3d-9e5f-8a7b6c5d4e3f",
"transactionCodeConfiguration": {
"inputMode": "numeric",
"description": "Please enter the one-time code that was sent to you via email."
},
"claims": {
"givenName": "John",
"familyName": "Doe",
"email": "john.doe@example.com",
"userId": "e7b8c2f1-4a3d-4e6b-9c2a-1f5d8e7a9b3c"
},
"claimsToPersist": ["userId"],
"expiresIn": {
"minutes": 5,
"seconds": 0
}
}
```
* `credentials` : This array includes a list of identifiers for mDoc credential configurations that
will be included in the credential offer. These identifiers are the `id` elements returned in the
response when you [create a Credential configuration](/docs/issuance/credential-configuration/overview).
To issue multiple credential formats of the same credential in a single flow, include all the
required credential configuration id elements in the request payload.
Providing an identifier of a non-mDoc credential configuration will result in an error.
* `userId` : Unique system generated identifier to reference the user for this offer. This can be
obtained by [searching for a user](/docs/api-reference/platform/users/searchUsers). If not
provided, a new user entity will be created.
* `transactionCodeConfiguration` : This object contains the configuration for the transaction code
that will be associated with this credential offer and must be provided by the holder when
claiming the credential. It includes the following properties:
* `inputMode` : The input mode for the transaction code. Currently only `numeric` is supported.
* `description` : A description of the transaction code that will be sent to the user as part of
the credential offer.
* `claims` : This object contains the claims that will be included in the issued credential.
* The claims must match the mapping defined in the Credential configuration.
* By default, claims data is only stored for the lifetime of the offer, unless you specify them in the optional `claimsToPersist` array.
* `claimsToPersist` : This array includes a list of claims that will be persisted against the [user object](/docs/issuance/users/overview) in the MATTR VII
database. These claims are then available for any future credential offers or issuance operations for this user. By default no claims are persisted, and it is recommended to consider carefully which claims to persist, if any, as this has implications for data privacy and security.
* `expiresIn` : Specifies when the offer will expire. Once the offer expires, the user can no longer
use it to claim a credential, and a new offer must be generated. The expiration period can include
any combination of `minutes` and `seconds`. By default, the offer expires in 5 minutes, and the
maximum allowed duration is 10 minutes.
*Response*
```json title="Response body"
{
"id": "b1a7c9e2-4d5f-4a6b-9e8c-2f3d4b5a6c7e", // [!code highlight]
"userId": "b7e2c8f4-1a2b-4c3d-9e5f-8a7b6c5d4e3f",
"uri": "openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fexample.com%22%2C%22credentials%22%3A%5B%222edaf985-fcc2-4448-9c8e-a04c6c7351c2%22%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22stukD6lg9c9tQ3jUCa32wVi1HI%2BQIVsFK%2FQPvC2CHRs%3D%22%2C%22tx_code%22%3A%7B%22length%22%3A6%2C%22input_mode%22%3A%22numeric%22%2C%22description%22%3A%22Please%20provide%20the%20one-time%20code%20that%20was%20sent%20via%20e-mail%22%7D%7D%7D%7D", // [!code highlight]
"expiresAt": "2025-05-01T00:01:00.000Z", // [!code highlight]
"transactionCode": 493536 // [!code highlight]
}
```
* `id` : Unique system generated identifier to reference this offer.
* `uri` : This is the URI that should be used by the intended holder to claim the credential. For a
Pre-authorized Code flow credential offers this URI includes the pre-authorized code which enables
the wallet to retrieve the credential without requiring the user to provide any additional
authentication.
* `expiresAt` : The date and time when the offer will expire. Once the offer expires, the user can
no longer use it to claim a credential, and a new offer must be generated.
* `transactionCode` : This is the transaction code that must be provided by the user when claiming
the credential. This code must be shared by the issuer with the holder via a separate and secure
channel.
### Send an offer URI [#send-an-offer-uri]
Once a credential offer URI is generated, you can send it to the intended holder in one of the
following methods:
* Send via a QR code.
* Send via a Deeplink.
* Send via a DID message.
Refer to [Claiming credential offers](/docs/issuance/credential-offer/overview#claiming-credential-offers) for more details on how to adjust the offer URI to use specific schemes and the resulting user experience.
You can use any of these methods regardless of the issuance flow you are using
(Authorization Code or Pre-authorized Code).
A common way to allow a digital wallet user to claim a credential is to encode the offer URI into a QR code. You could even print out the QR code.
You can use a tool similar to the following to convert the URI to a QR code (make sure you use the
`Plain text` option where available):
* [https://www.the-qrcode-generator.com/](https://www.the-qrcode-generator.com/)
* [http://goqr.me/api/](http://goqr.me/api/)
* [https://www.qr-code-generator.com/](https://www.qr-code-generator.com/)
MATTR is not affiliated with any of these service providers and cannot vouch
for their offerings.
Once the QR code is created, you can send it to the intended holder via your preferred communication
channel.
**Best practices**
* Make sure the QR code is large enough in size to be resolvable by a phone camera; 200px square is
generally sufficient.
You can enable the intended holder to open the offer URI directly from their mobile device by creating a deep link.
1. Perform base64url encoding of the offer URI.
2. Use the output (the offer URI encoded as base64url) to create a link of the following structure:
`global.mattr.wallet://accept/{base64Url(openid-credential-offer://...)}`
For example:
`global.mattr.wallet://accept/b3BlbmlkLWNyZWRlbnRpYWwtb2ZmZXI6Ly8_Y3JlZGVudGlhbF9vZmZlcj0lN0IlMjJjcmVkZW50aWFsX2lzc3VlciUyMiUzQSUyMmh0dHBzJTNBJTJGJTJGdGVuYW50LnZpaS5tYXR0ci5nbG9iYWwlMjIlMkMlMjJjcmVkZW50aWFscyUyMiUzQSU1QiUyMjIwZDZiYmU2LWE5NzgtNDQ3Yy1iNWJkLWYzM2I2ZGNhMTllMiUyMiU1RCU3RA`
3. Once the deep link is created, you can send it to the recipient via your preferred communication
channel.
**MATTR GO deep-links**
When sending a deep link to a [MATTR GO Hold](/docs/holding/go-hold/getting-started), replace the scheme with your app
bundle ID. The following example shows a deep link for a MATTR GO wallet that has
`com.example.wallet` as its app bundle ID, as shown in the example below:
`com.example.wallet://accept/b3BlbmlkLWNyZWRlbnRpYWwtb2ZmZXI6Ly8_Y3JlZGVudGlhbF9vZmZlcj0lN0IlMjJjcmVkZW50aWFsX2lzc3VlciUyMiUzQSUyMmh0dHBzJTNBJTJGJTJGdGVuYW50LnZpaS5tYXR0ci5nbG9iYWwlMjIlMkMlMjJjcmVkZW50aWFscyUyMiUzQSU1QiUyMjIwZDZiYmU2LWE5NzgtNDQ3Yy1iNWJkLWYzM2I2ZGNhMTllMiUyMiU1RCU3RA`
**MATTR Pi deep-links**
For MATTR Pi Holder SDK customers, refer to your own implementation for deep linking requirements
and best practices.
To send the Credential offer as a DID message you must first encrypt it and then send it as an
encrypted message.
**Step 1: Encrypt a Credential offer**
Make a request of the following structure to
[encrypt the Credential offer](/docs/api-reference/platform/messaging/encryptMessage):
```http title="Request"
POST /v1/messaging/encrypt
```
```json title="Request body"
{
"senderDidUrl": "did:web:learn.vii.au01.mattr.global#z6LShWb1DVC2gkxoQ91VwHmNhci2A4NdVH4srFvLiTP6ETBK",
"recipientDidUrls": [
"did:key:z6MkgmEkNM32vyFeMXcQA7AfQDznu47qHCZpy2AYH2Dtdu1d"
],
"payload": {
"id": "731961f2-bdc3-4f1e-8d59-cc308fd60ec8",
"type": "https://mattr.global/schemas/verifiable-credential/offer/OidcCredentialProvider",
"from": "did:web:organization.com",
"created_time": 1616466734,
"body": {
"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%22request_parameters%22%3A%7B%22login_hint%22%3A%22user%40example.com%22%2C%22prompt%22%3A%22login%22%7D%7D"
}
}
}
```
* `senderDidUrl` : Use the [Issuer DID](#prerequisites).
* `recipientDidUrls` : Use the [Subject DID](#prerequisites).
* `payload` :
* `id` : Use the [Credential configuration `id`](#prerequisites).
* `type` : Use the
`https://mattr.global/schemas/verifiable-credential/offer/OidcCredentialProvider` to indicate
that this message includes a Credential offer.
* `from` : Use the [Issuer DID](#prerequisites).
* `created_time` : Time of creation (Epoch Unix timestamp). The value must be a number and not a
string, otherwise the holder will not be able to accept the Credential offer.
* `body` :
* `uri` : Use the credential offer URI, obtained when the offer was
[created](#generate-an-offer-uri).
*Response*
```json title="Response body"
{
"jwe": {
"protected": "eyJhbGciOiJYQzIwUCJ9",
"recipients": [
{
"header": {
"alg": "ECDH-1PU+A256KW",
"kid": "did:key:z6MkgmEkNM32vyFeMXcQA7AfQDznu47qHCZpy2AYH2Dtdu1d#z6LSsvqSJkBvVEsDC8cxMHuQ3sKoLRMXB1MdtoLrMUq6A8Rg",
"epk": {
"kty": "OKP",
"crv": "X25519",
"x": "JOLnYaD7L-Rszz7fczPhn6MkNre25PUsztzB1RHoz14"
},
"skid": "did:key:z6MkreuqFq6WrwozTeGKuUDz8bniTFRNAg8f3ZB862YdLp7v#z6LScyz3YLToyoKwZE6Tfq65hgZUkZdHrC4ZqohcUH9X6Twx"
},
"encryption_key": "ag5iKzjJOth9Wa68dCVKJW_vnO_Ga0zSJgQp5rIUg69HCzIjuNYhDg"
}
],
"ciphertext": "xpW-D6sDPpWc_jk87nEyxPX7JQV8_OZpaQft7ySQ5XmNhoj-lQyDkXDncOCyhB7yMSdZrRBNQjKxlEbpY_WLk1hBoWfsTeszVSAuFbX_VKUSJ7GR6rcnWGVNgDfKS8GsyC_owtswXatkF_65_mzFOygctkUmd2eI5bcpQpWjhw2vqnvnWkb7l2J27aWFF_c9cu52dB559j8lwLYyYC9oSMgV5piB6ppfrWBGo_DigjxvJcAYcjFYqFcT6A1nphPhwVTQ2HNfJodbQoseHub8UQdG4qAOcggq5DI84tbqor1SU9rdPH03jPkLgoO_aeXyJg5meITXoFSiu_tRfvf8QQ6vKq6pkTTXs8zKXcBCGhGIyKBNBG4R4RIY1UffTMnJQQQGBble3P06pGOnsnSop0BtygelB9M0ZEwnAUSAQqN1RR4AQwWcn9nH6hHEu1pMhSvhCuFNAPWS-hg24JGGw8Xe3EEZlLH0PM8qpUAfksPq",
"iv": "FJq5zKvuPiUQIdRcMtiChHCJByuY8XK9",
"tag": "u8kT0VAAtTswjGXxNpuX0g=="
}
}
```
**Step 2: Send an encrypted Credential offer**
Make a request of the following structure to
[send the encrypted Credential offer](/docs/api-reference/platform/messaging/sendMessage):
```http title="Request"
POST /v1/messaging/send
```
```json title="Request body"
{
"to": "did:key:z6MkgmEkNM32vyFeMXcQA7AfQDznu47qHCZpy2AYH2Dtdu1d",
"message": {
"protected": "eyJhbGciOiJYQzIwUCJ9",
"recipients": [
{
"header": {
"alg": "ECDH-1PU+A256KW",
"kid": "did:key:z6MkgmEkNM32vyFeMXcQA7AfQDznu47qHCZpy2AYH2Dtdu1d#z6LSsvqSJkBvVEsDC8cxMHuQ3sKoLRMXB1MdtoLrMUq6A8Rg",
"epk": {
"kty": "OKP",
"crv": "X25519",
"x": "JOLnYaD7L-Rszz7fczPhn6MkNre25PUsztzB1RHoz14"
},
"skid": "did:key:z6MkreuqFq6WrwozTeGKuUDz8bniTFRNAg8f3ZB862YdLp7v#z6LScyz3YLToyoKwZE6Tfq65hgZUkZdHrC4ZqohcUH9X6Twx"
},
"encryption_key": "ag5iKzjJOth9Wa68dCVKJW_vnO_Ga0zSJgQp5rIUg69HCzIjuNYhDg"
}
],
"ciphertext": "xpW-D6sDPpWc_jk87nEyxPX7JQV8_OZpaQft7ySQ5XmNhoj-lQyDkXDncOCyhB7yMSdZrRBNQjKxlEbpY_WLk1hBoWfsTeszVSAuFbX_VKUSJ7GR6rcnWGVNgDfKS8GsyC_owtswXatkF_65_mzFOygctkUmd2eI5bcpQpWjhw2vqnvnWkb7l2J27aWFF_c9cu52dB559j8lwLYyYC9oSMgV5piB6ppfrWBGo_DigjxvJcAYcjFYqFcT6A1nphPhwVTQ2HNfJodbQoseHub8UQdG4qAOcggq5DI84tbqor1SU9rdPH03jPkLgoO_aeXyJg5meITXoFSiu_tRfvf8QQ6vKq6pkTTXs8zKXcBCGhGIyKBNBG4R4RIY1UffTMnJQQQGBble3P06pGOnsnSop0BtygelB9M0ZEwnAUSAQqN1RR4AQwWcn9nH6hHEu1pMhSvhCuFNAPWS-hg24JGGw8Xe3EEZlLH0PM8qpUAfksPq",
"iv": "FJq5zKvuPiUQIdRcMtiChHCJByuY8XK9",
"tag": "u8kT0VAAtTswjGXxNpuX0g=="
}
}
```
* `to` : Use the intended holder's [Subject DID](#prerequisites).
* `message` : Use the content of the `jwe` object from the previous step's response (do not include
the `jwe` property name, just its content).
*Response*
A `200` response indicates that the message payload was sent to the service endpoint of the
dereferenced DID Document (or the default MATTR service endpoint).
### Accept a Credential offer [#accept-a-credential-offer]
Once the Credential offer is shared with the intended holder, it is up to them to use their digital
wallet to accept the offer and claim the credential.
The credential is only issued after the intended holder accepts the Credential offer.
# Credential offer
URL: /docs/issuance/credential-offer/overview
## Overview [#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](/docs/issuance/credential-configuration/overview) 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](/docs/issuance/authorization-code/overview): 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](/docs/issuance/pre-authorized-code/overview): 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.
**Claiming behavior:**
* **Authorization Code flow offers** can be claimed multiple times by different users. Each user authenticates independently during the issuance workflow, allowing the same offer URI to issue credentials with different user-specific data to multiple holders.
* **Pre-authorized Code flow offers** are generated for a specific user after out-of-band authentication. Once successfully claimed, the pre-authorized code is consumed and the offer becomes invalid. The offer cannot be claimed again, even if the same user attempts to claim it.
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 [#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 [#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 OID4VCI 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 [#choosing-the-right-uri-scheme]
Select a URI scheme based on your requirements:
| Requirement | Recommended Scheme |
| ---------------------------------------------------------- | ----------------------- |
| Maximum interoperability - work with any compatible wallet | Standard OID4VCI scheme |
| Target a specific wallet without domain verification | Private-use URI scheme |
| Ensure only your specific app can handle offers | Claimed HTTPS scheme |
| Production deployment with strict security requirements | Claimed HTTPS scheme |
| Development and testing | Standard OID4VCI scheme |
### Implementing URI schemes [#implementing-uri-schemes]
The following sections show you how to implement each URI scheme type as an issuer.
#### Standard OID4VCI custom scheme [#standard-oid4vci-custom-scheme]
The OID4VCI 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](/docs/issuance/credential-offer/guide#generate-an-offer-uri) using the MATTR VII API.
2. Use the returned `uri` field directly:
```json title="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"
}
```
```json title="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
}
```
3. Present the offer to users as a [QR code](#displayed-as-a-qr-code-cross-device) or [deep link](#displayed-as-a-hyperlink-or-button-same-device).
**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](/docs/holding/credential-claiming-guides/handling-uri-schemes#standard-openid4vci-custom-scheme) for implementation details.
#### Private-use URI scheme [#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](/docs/issuance/credential-offer/guide#generate-an-offer-uri) using the MATTR VII API.
2. Extract the `uri` field from the response.
3. Transform the URI to use your custom scheme:
```javascript title="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:
```javascript title="Transform to custom scheme with query parameter"
const customUri = `com.example.wallet://accept?offer=${encodedOffer}`;
// Result: com.example.wallet://accept?offer=b3BlbmlkLWNyZWRlbnRpYWwtb2ZmZXI6Ly8_Y3JlZGVudGlhbF9vZmZlcj0lN0IlMjJjcmVkZW50aWFsX2lzc3Vlci4uLg
```
4. Present the transformed URI to users as a [QR code](#displayed-as-a-qr-code-cross-device) or [deep link](#displayed-as-a-hyperlink-or-button-same-device).
**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 OID4VCI offer URI.
* Process the offer using the standard OID4VCI flow.
See the [holder guide](/docs/holding/credential-claiming-guides/handling-uri-schemes#private-use-uri-scheme) for implementation details.
#### Claimed HTTPS scheme (App Links / Universal Links) [#claimed-https-scheme-app-links--universal-links]
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`:
```json title="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`:
```json title="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](/docs/issuance/credential-offer/guide#generate-an-offer-uri) using the MATTR VII API.
2. Extract the `uri` field from the response.
3. Transform the URI to use your HTTPS domain:
```javascript title="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
```
4. Present the transformed URI to users as a [QR code](#displayed-as-a-qr-code-cross-device) or [deep link](#displayed-as-a-hyperlink-or-button-same-device).
**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 OID4VCI offer URI.
* Process the offer using the standard OID4VCI flow.
See the [holder guide](/docs/holding/credential-claiming-guides/handling-uri-schemes#claimed-https-scheme-app-links--universal-links) for implementation details.
### Security considerations [#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.
[Wallet Attestation](/docs/issuance/credential-issuance/wallet-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 wallet 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](mailto:dev-support@mattr.global) if you need help configuring restricted redirect URIs for your tenant.
### Presenting credential offers to users [#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.
#### Displayed as a hyperlink or button (same-device) [#displayed-as-a-hyperlink-or-button-same-device]
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) [#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 to create an Apple digital pass template
URL: /docs/issuance/cwt-credential-templates/apple-templates
An Apple digital pass template is required to
[format a CWT or Semantic CWT credential as an Apple digital pass](/docs/issuance/cwt-direct-issuance#format-the-signed-cwt-credential).
## Overview [#overview]
Creating an Apple digital pass template comprises the following steps:
1. [Design a template](#design-a-template)
2. [Create a MATTR VII template](#create-a-mattr-vii-template)
## Prerequisites [#prerequisites]
* Credentials associated with your organization's
[Apple developer account](https://developer.apple.com/programs/).
**Sample templates**: You can download the following sample templates to better understand how to
structure your own templates as you follow along this guide:
* CWT credential
[sample Apple digital pass template](https://github.com/mattrglobal/sample-apps/tree/master/credential-templates/compact/apple-pass/WorkingAtHeightsCertVC).
* Semantic CWT credential
[sample Apple digital pass template](https://github.com/mattrglobal/sample-apps/tree/master/credential-templates/compact-semantic/apple-pass/WorkingAtHeightsCertVC).
### Design a template [#design-a-template]
Bundle the following digital pass assets into a `.zip` file:
* `pass.json` : This *required* file contains pass information and identifiers.
* `footer.png` : Displayed on the front of the pass near the barcode. Only available on boarding
passes.
* `icon.png` : Displayed in notifications and in emails that have a pass attached, and on the lock
screen. When it is displayed, the icon gets a shine effect and rounded corners.
* `thumbnail.png` : Displayed on the front of the pass. Only available on generic passes. For
example, on a membership card, the thumbnail could be used for a cardholder picture.
* `logo.png` : Displayed on the top-left corner of the front of the pass.
* `strip.png` : Displayed behind the primary fields on the front of the pass. Only available on
store cards, event tickets and coupons.
The template `.zip` file cannot be larger than 1mb.
Different sized images (e.g. @2x, @3x) can be included in the bundle.
Read more about the visual layout of Apple digital passes and [suggested design
guidelines](https://developer.apple.com/design/human-interface-guidelines/wallet#Designing-passes).
##### pass.json [#passjson]
The contents of `pass.json` are described in detail in
[Apple's developer documentation](https://developer.apple.com/documentation/walletpasses/pass). The
following is an example `pass.json` file:
```json title="Example pass.json file"
{
"formatVersion": 1,
"organizationName": "Advanced Safety Training",
"description": "HS.278 Working at Heights Certification",
"labelColor": "rgb(45, 45, 45)",
"foregroundColor": "rgb(45, 45, 45)",
"backgroundColor": "rgb(202, 202, 202)",
"sharingProhibited": true,
"voided": false,
"barcode": {
"format": "PKBarcodeFormatQR",
"messageEncoding": "iso-8859-1",
"message": "{{encoded}}",
"altText": "Exp: {{ date decoded.expiry 'dd MMM yyyy' }}"
},
"storeCard": {
"headerFields": [
{
"key": "codeHeader",
"label": "Certification",
"value": "{{ decoded.code }}"
}
],
"primaryFields": [],
"secondaryFields": [
{
"key": "nameSecondary",
"label": "Name",
"value": "{{ decoded.name }}"
},
{
"key": "certificationLevelSecondary",
"label": "Certified for",
"value": "{{ decoded.certificationLevel }}"
}
],
"auxiliaryFields": [],
"backFields": [
{
"key": "nameBack",
"label": "Name",
"value": "{{ decoded.name }}"
},
{
"key": "certificationNameBack",
"label": "Certification",
"value": "{{ decoded.certificationName }}"
}
]
}
}
```
* `formatVersion` (*required*): File format version. The value must be 1.
* `organizationName` (*required*): Name of the organization that created and signed the pass.
* `description` (*required*): Pass description, used by iOS accessibility technologies.
* `labelColor` (*optional*): Label text color, specified as a CSS-style RGB triple. When omitted,
label color is automatically determined.
* `foregroundColor` (*optional*): Foreground pass color, specified as a CSS-style RGB triple. When
omitted, label color is automatically determined.
* `sharingProhibited` : Set to `false` to disable the Share Pass option
* `voided` : Indicates that the pass is void. For example, a one time use coupon that has been
redeemed. The default value is `false`.
* `barcode` :
* `format` : Must be set to `PKBarcodeFormatQR`.
* `messageEncoding` : Must be set to `iso-8859-1`.
* `storeCard` : This is the style key which corresponds with the pass's style. Can be either
`storeCard` (as shown in the example above), `generic`, `eventTicket`, `coupon` or a
`boardingPass`. Each style key has different components. Below is an example of a `storeCard`
pass structure:
* `headerFields` : Fields to be displayed in the header on the front of the pass. Use header
fields sparingly; unlike all other fields, they remain visible when a stack of passes is
displayed.
* `primaryFields` : Fields to be displayed prominently on the front of the pass.
* `secondaryFields` : Fields to be displayed on the front of the pass.
* `auxiliaryFields` : Additional fields to be displayed on the front of the pass.
* `backFields` : Fields to be on the back of the pass.
* `transitType` : this is required for boarding passes and not allowed otherwise. Must be one
of the following: `PKTransitTypeAir`, `PKTransitTypeBoat`, `PKTransitTypeBus`,
`PKTransitTypeGeneric` or `PKTransitTypeTrain`.
* Each field must contain a set of standard field dictionary keys:
* `key` : the key must be unique within the scope of the entire pass.
* `label` : label text for the field.
* `value` : the value of the field.
### Create a MATTR VII template [#create-a-mattr-vii-template]
Make a `multipart/form-data` request with the template `.zip` file included as a binary file to
[create a CWT credential Apple digital pass template](/docs/issuance/direct-issuance-api-reference/cwt-apple-pass-templates#create-a-cwt-credential-apple-pass-template):
```http title="Request"
POST /v2/credentials/compact/digital-pass/apple/templates
```
You can make a similar request to a different endpoint to
[create a Semantic CWT credential Apple digital pass template](/docs/issuance/direct-issuance-api-reference/semantic-cwt-apple-pass-templates#create-a-semantic-cwt-credential-apple-pass-template):
```http title="Request"
POST /v2/credentials/compact-semantic/digital-pass/apple/templates
```
*Request keys*
| Key | Type | Description |
| :------------------- | :--: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `template` | File | Attach your `template.zip` file. |
| `name` | Text | Insert a name to identify this Apple digital pass template. |
| `fileName` | Text | Insert the file name that will be assigned to Apple digital passes created from this template. |
| `teamIdentifier` | Text | The [Team ID](https://developer.apple.com/help/account/manage-your-team/locate-your-team-id/) for the Apple Developer Program account that registered the pass type identifier. |
| `passTypeIdentifier` | Text | The pass type identifier that’s [registered](https://developer.apple.com/documentation/walletpasses/building_a_pass) with Apple. The value must be the same as the distribution certificate used to sign the pass. |
| `wwdr` | Text | Apple G1 or G4 worldwide developer relations intermediate certificate. |
| `signerCert` | Text | Apple pass signer [certificate](https://help.apple.com/developer-account/#/devbfa00fef7). |
| `signerKey` | Text | The encrypted key of the Apple pass signer certificate. |
| `signerPassphrase` | Text | Passphrase for the encrypted key. |
*Response*
```json title="Response body"
{
"id": "1b04f0ee-8e3e-4153-a0e0-8603a10e7f0a", // [!code focus]
"name": "example",
"passType": "apple",
"metadata": {
"fileName": "example.pkpass",
"teamIdentifier": "xxxxx",
"passTypeIdentifier": "xxxxx"
}
}
```
* `id` : This is a unique identifier for this Apple digital pass template. You will use it as the
`templateId` when
[formatting a CWT credential as an Apple digital pass](/docs/issuance/cwt-direct-issuance#format-the-signed-cwt-credential).
* All other fields in the response include information retrieved from your Apple digital pass
template.
### Troubleshooting [#troubleshooting]
Errors may occur in the following scenarios:
* Unable to decompress the template `.zip` file.
* `pass.json` is missing from the bundle or it is malformed.
* Required fields in `pass.json` are not available.
* The file name contains a special character that is not supported by the Apple pass template
endpoint.
* The bundle file exceeds the maximum size allowed (1mb).
If you ever need to update any issuer information such as `teamIdentifier`, `passTypeIdentifier`,
`wwdr`, `signerCert`, `signerKey` or `signerKeyPassphrase`, it is highly recommended to create a new
template. However, you can also
[update an existing template](/docs/issuance/direct-issuance-api-reference/cwt-apple-pass-templates#update-a-cwt-credential-apple-pass-template).
# How to create a Google digital pass template
URL: /docs/issuance/cwt-credential-templates/google-templates
A Google digital pass template is required to
[format a CWT or Semantic CWT credential as a Google digital pass](/docs/issuance/cwt-direct-issuance#format-the-signed-cwt-credential).
## Overview [#overview]
Creating a Google digital pass template comprises the following steps:
1. [Design a template](#design-a-template)
2. [Create a MATTR VII template](#create-a-mattr-vii-template)
## Prerequisites [#prerequisites]
* Credentials associated with your organization's
[Google account](https://cloud.google.com/iam/docs/service-accounts).
**Sample templates**: You can download the following sample templates to better understand how to structure your own templates as you follow along this guide:
* CWT credential
[sample Google digital pass template](https://github.com/mattrglobal/sample-apps/tree/master/credential-templates/compact/google-pass/WorkingAtHeightsCertVC).
* Semantic CWT credential
[sample Google digital pass template](https://github.com/mattrglobal/sample-apps/tree/master/credential-templates/compact-semantic/google-pass/WorkingAtHeightsCertVC).
Note that our provided template offers greater flexibility in designing the digital pass, while
the suggested template in the code snippets below would be easier to get started with. Either
way. it is highly recommended to use Google's
[Generic Pass template](https://developers.google.com/wallet/generic/resources/template).
### Design a template [#design-a-template]
Create your template in a JSON file and then bundle it into a `.zip` file. The JSON file contains
the digital pass information, identifiers and value mappings. It is structured as follows:
The template
`.zip`
file cannot be larger than 1mb.
```json title="Example template.json file"
{
"genericClass": {
"textModulesData": [
{
"header": "Name",
"body": "{{ decoded.name }}"
},
{
"header": "Certification",
"body": "{{ decoded.certificationName }}"
},
{
"header": "Code",
"body": "{{ decoded.code }}"
},
{
"header": "Certified for",
"body": "{{ decoded.certificationLevel }}"
},
{
"header": "Expires on",
"body": "{{ date decoded.expiry 'dd MMM yyyy' }}"
},
{
"header": "Issued by",
"body": "{{ decoded.issuerName }}"
}
]
},
"genericObject": {
"genericType": "GENERIC_TYPE_UNSPECIFIED",
"cardTitle": {
"defaultValue": {
"language": "en-US",
"value": "Working at Heights Certification"
}
},
"header": {
"defaultValue": {
"language": "en-US",
"value": "{{ decoded.certificationName }}"
}
},
"subHeader": {
"defaultValue": {
"language": "en-US",
"value": "Advanced Safety Training"
}
},
"logo": {
"sourceUri": {
"uri": "https://YOUR_WEB_DOMAIN/LINK_TO_LOGO_IMAGE.png",
"description": ""
}
},
"hexBackgroundColor": "#CACACA",
"heroImage": {
"sourceUri": {
"uri": "https://YOUR_WEB_DOMAIN/LINK_TO_HERO_IMAGE.png",
"description": ""
}
},
"barcode": {
"type": "QR_CODE",
"value": "{{encoded}}",
"alternateText": "QR code"
}
}
}
```
* `genericClass` : Contains common data across objects. Text fields on the pass face are defined
here. Each text module contains a header and a body for each row. To add text to the pass
details, set the values in the `textModulesData`. Refer to
[Google Wallet GenericClass documentation](https://developers.google.com/wallet/reference/rest/v1/genericclass)
for more options.
* `genericObject` : Represents each Generic Pass a user has in their Google Wallet app. It
contains a number of configurable attributes such as card title, header, sub header, logo,
background color, hero image and barcode. The image assets should be uploaded and publicly
accessible. Refer to
[Google Wallet GenericObject documentation](https://developers.google.com/wallet/reference/rest/v1/genericobject)
for more options.
### Create a MATTR VII template [#create-a-mattr-vii-template]
Make a `multipart/form-data` request with the template `.zip` file included as a binary file to
[create a CWT credential Google digital pass template](/docs/issuance/direct-issuance-api-reference/cwt-google-pass-templates#create-a-cwt-credential-google-pass-template):
```http title="Request"
POST /v2/credentials/compact/digital-pass/google/templates
```
You can make a similar request to a different endpoint to
[create a Semantic CWT credential Google digital pass template](/docs/issuance/direct-issuance-api-reference/semantic-cwt-google-pass-templates#create-a-semantic-cwt-credential-google-pass-template):
```http title="Request"
POST /v2/credentials/compact-semantic/digital-pass/google/templates
```
*Request keys*
| Key | Type | Description |
| :-------------------------- | :--: | :-------------------------------------------------------------------------------------------------- |
| `template` | File | Include your template `.zip` file. |
| `name` | Text | Insert a name to identify this Google digital pass template. |
| `issuerId` | Text | Google Wallet Pass signer issuer ID. |
| `serviceAccountClientEmail` | Text | Email address of the Google Cloud Platform service account for accessing the Google Pay Passes API. |
| `serviceAccountPrivateKey` | Text | Private key PEM of the Google Cloud Platform service account. |
Learn how to grant access to your service account to call the [Google Wallet
API](https://developers.google.com/wallet/generic/getting-started/onboarding-guide#3_create_a_service_account).
*Response*
```json title="Response body"
{
"id": "0793fade-bd27-46a8-8dfe-67c4d3e9cf09", // [!code focus]
"name": "example",
"passType": "google",
"metadata": {
"issuerId": "xxxxx",
"serviceAccountClientEmail": "xxxxx",
"payPassId": "xxxxx"
}
}
```
* `id` : This is a unique identifier for this Apple digital pass template. You will use it as the
`templateId` when
[formatting a CWT credential as a Google digital pass](/docs/issuance/cwt-direct-issuance#format-the-signed-cwt-credential).
* All other fields in the response include information retrieved from your Google digital pass
template.
### Troubleshooting [#troubleshooting]
Errors may occur in the following scenarios:
* Unable to decompress the template `.zip` file.
* `template.json` is missing from the bundle or it is malformed.
* The template `.zip` file exceeds the maximum size allowed (1mb).
To avoid potential impact on previously generated Google digital passes,
[updating an existing template](/docs/issuance/direct-issuance-api-reference/cwt-google-pass-templates#update-a-cwt-credential-google-pass-template)
will always create a new template in the Google Pay Business Console.
# How to create a PDF template
URL: /docs/issuance/cwt-credential-templates/pdf-templates
A PDF template is required to
[format a CWT or Semantic CWT credential as a PDF document](/docs/issuance/cwt-direct-issuance#format-the-signed-cwt-credential).
## Overview [#overview]
Creating a PDF template comprises the following steps:
1. [Design a template](#design-a-template)
2. [Create a MATTR VII template](#create-a-mattr-vii-template)
**Sample templates**: You can download the following sample templates to better understand how to structure your own templates as you follow along this guide:
* CWT credential
[sample PDF template](https://github.com/mattrglobal/sample-apps/tree/master/credential-templates/compact/pdf/WorkingAtHeightsCertVC).
* Semantic CWT credential
[sample PDF template](https://github.com/mattrglobal/sample-apps/tree/master/credential-templates/compact-semantic/pdf/WorkingAtHeightsCertVC).
### Design a template [#design-a-template]
Bundle the following template components into a `.zip` file:
* [`template.pdf`](#templatepdf): This is a required PDF file used as the PDF template.
* [`config.json`](#configjson): This is a required JSON file used for field mappings.
* `fonts` : This is an optional folder that includes any custom fonts being used in the template
in `.otf` or `.ttf` format.
The template `.zip` file cannot be larger than 700kb.
##### template.pdf [#templatepdf]
* Maximum size of `template.pdf` is **700kb**.
* The template must have a `qrCode` button field. The QR code is generated from the payload and
there is no value mapping required for this field.
* All other fields in the template should be plain text fields
* Other field types (e.g. checkboxes, radio buttons, list boxes, dropdown boxes) are not
supported.
* No macros, rules, formulation or calculations are supported.
* It is possible to have identical claims in the same PDF. The field names be suffixed (e.g.
`firstName#1`, `firstName#2`).
* Multi-page templates are supported as long as the `qrCode` field is on the first page.
* Reading order for each field should be specified to support accessibility. Unselect
`Display like elements in a single block`.
##### config.json [#configjson]
This JSON file should be structured as follows:
```json title="Example config.json file"
{
"name": "SamplePDF_WorkingAtHights",
"fileName": "{{ vc.credentialSubject.code }}_{{ vc.credentialSubject.name }}",
"metadata": {
"title": "{{ vc.credentialSubject.certificationName }} Certification – {{ vc.credentialSubject.name }}"
},
"fonts": [
{
"name": "PublicSans-Regular",
"fileName": "PublicSans-Regular.ttf"
},
{
"name": "PublicSans-Bold",
"fileName": "PublicSans-Bold.ttf"
}
],
"fields": [
{
"key": "name",
"value": "{{ vc.credentialSubject.name }}",
"isRequired": true,
"alternativeText": "{{ vc.credentialSubject.name }}",
"fontName": "PublicSans-Regular"
},
{
"key": "code",
"value": "{{ vc.credentialSubject.code }}",
"isRequired": true,
"alternativeText": "{{ vc.credentialSubject.code }}",
"fontName": "PublicSans-Bold"
}
]
}
```
* `name` : Template name.
* `fileName` : Use values from your `template.pdf` to set the generated PDF file name.
* `metadata` : Use values from your `template.pdf` to set the generated PDF metadata.
* `fonts` : This array includes custom fonts that are used in your PDF template. Note that these
custom fonts must be included in a `fonts` folder in your `template.zip` bundle for the template
to be valid.
* `name` : The name of the font to be referenced by fields using it.
* `fileName` : The name of the font `.otf`/`.ttf` file in the fonts folder.
* `fields` : This array includes fields that are defined in your `template.pdf` file: - `key` : Field name in the `template.pdf` file. - `value` : Mapped claim from the credential payload. - `isRequired` : When set to `true`, the value must be provided in the credential payload to
generate a valid PDF. If it is not provided, an error would occur. - `alternativeText` : Alternative text to support accessibility. Must be specified for every
field except for `qrCode`. If the value is defined in both `template.pdf` and `config.json`,
the value from `config.json` is used. - `fontName` : Custom font name to display the field. When no font is specified *Helvetica* is
used by default. Note that this default font only supports Windows-1252 encoding character
sets. Refer to
[Internationalization implementation considerations](/docs/concepts/cwt/implementation#internationalization---how-can-i-support-or-display-multiple-languages)
for more information.
### Create a MATTR VII template [#create-a-mattr-vii-template]
Make a request of the following structure with the template `.zip` file included as a binary file to
[create a CWT credential PDF template](/docs/issuance/direct-issuance-api-reference/cwt-pdf-templates#create-a-cwt-credential-pdf-template):
```http title="Request"
POST /v2/credentials/compact/pdf/templates
Header: Content-Type: application/zip
```
You can make a similar request to a different endpoint to
[create a Semantic CWT credential PDF template](/docs/issuance/direct-issuance-api-reference/semantic-cwt-pdf-templates#create-a-semantic-cwt-credential-pdf-template):
```http title="Request"
POST /v2/credentials/compact-semantic/pdf/templates
Header: Content-Type: application/zip
```
*Response*
```json title="Request body"
{
"id": "682f203a-f5bd-4304-95e3-c8c708e90d26", // [!code focus]
"name": "SamplePDF_WorkingAtHights",
"fileName": "{{ vc.credentialSubject.code }}_{{ vc.credentialSubject.name }}",
"fonts": [
{
"name": "PublicSans-Regular",
"fileName": "PublicSans-Regular.ttf"
},
{
"name": "PublicSans-Bold",
"fileName": "PublicSans-Bold.ttf"
}
],
"fields": [
{
"key": "name",
"value": "{{ vc.credentialSubject.name }}",
"isRequired": true,
"alternativeText": "{{ vc.credentialSubject.name }}",
"fontName": "PublicSans-Regular"
},
{
"key": "code",
"value": "{{ vc.credentialSubject.code }}",
"isRequired": true,
"alternativeText": "{{ vc.credentialSubject.code }}",
"fontName": "PublicSans-Bold"
},
{
"key": "certificationName",
"value": "{{ vc.credentialSubject.certificationName }}",
"isRequired": true,
"alternativeText": "{{ vc.credentialSubject.certificationName }}",
"fontName": "PublicSans-Bold"
},
{
"key": "certificationLevel",
"value": "{{ vc.credentialSubject.certificationLevel }}",
"isRequired": true,
"alternativeText": "{{ vc.credentialSubject.certificationLevel }}",
"fontName": "PublicSans-Regular"
},
{
"key": "expiry",
"value": "{{ date vc.credentialSubject.expiry 'dd MMM yyyy' }}",
"isRequired": true,
"alternativeText": "{{ date vc.credentialSubject.expiry 'PPP' }}",
"fontName": "PublicSans-Regular"
}
],
"metadata": {
"title": "{{ vc.credentialSubject.certificationName }} Certification – {{ vc.credentialSubject.name }}"
}
}
```
* `id` : This is a unique identifier for this PDF template. You will use it as the `templateId`
when
[formatting a CWT credential as a PDF](/docs/issuance/cwt-direct-issuance#format-the-signed-cwt-credential).
* All other fields in the response include information retrieved from your PDF template.
### Troubleshooting [#troubleshooting]
Errors may occur in the following scenarios:
* Unable to decompress the template `.zip` file.
* Either `template.pdf` or `config.json` are missing from the bundle.
* Either `template.pdf` or `config.json` are malformed.
* Custom fonts are used but not provided in `template.zip`.
* Either the template `.zip` file or `template.pdf` exceed the maximum allowed file size (700kb).
* Fields set as required in `config.json` are not available in `template.pdf`.
# Apple Pass templates
URL: /docs/issuance/direct-issuance-api-reference/cwt-apple-pass-templates
## Create a CWT credential Apple Pass template [#create-a-cwt-credential-apple-pass-template]
## Retrieve all CWT credential Apple Pass templates [#retrieve-all-cwt-credential-apple-pass-templates]
## Retrieve a CWT credential Apple Pass template [#retrieve-a-cwt-credential-apple-pass-template]
## Update a CWT credential Apple Pass template [#update-a-cwt-credential-apple-pass-template]
## Delete a CWT credential Apple Pass template [#delete-a-cwt-credential-apple-pass-template]
# Google Pass templates
URL: /docs/issuance/direct-issuance-api-reference/cwt-google-pass-templates
## Create a CWT credential Google Pass template [#create-a-cwt-credential-google-pass-template]
## Retrieve all CWT credential Google Pass templates [#retrieve-all-cwt-credential-google-pass-templates]
## Retrieve a CWT credential Google Pass template [#retrieve-a-cwt-credential-google-pass-template]
## Update a CWT credential Google Pass template [#update-a-cwt-credential-google-pass-template]
## Delete a CWT credential Google Pass template [#delete-a-cwt-credential-google-pass-template]
# Issuance
URL: /docs/issuance/direct-issuance-api-reference/cwt-issuance
## Sign a CWT credential [#sign-a-cwt-credential]
## Format a CWT credential as a QR code [#format-a-cwt-credential-as-a-qr-code]
## Format a CWT credential as a PDF [#format-a-cwt-credential-as-a-pdf]
## Format a CWT credential as an Apple Pass [#format-a-cwt-credential-as-an-apple-pass]
## Format a CWT credential as a Google Pass [#format-a-cwt-credential-as-a-google-pass]
# PDF templates
URL: /docs/issuance/direct-issuance-api-reference/cwt-pdf-templates
## Create a CWT credential PDF template [#create-a-cwt-credential-pdf-template]
## Retrieve all CWT credential PDF templates [#retrieve-all-cwt-credential-pdf-templates]
## Retrieve a CWT credential PDF template [#retrieve-a-cwt-credential-pdf-template]
## Update a CWT credential PDF template [#update-a-cwt-credential-pdf-template]
## Delete a CWT credential PDF template [#delete-a-cwt-credential-pdf-template]
# Apple Pass templates
URL: /docs/issuance/direct-issuance-api-reference/semantic-cwt-apple-pass-templates
## Create a Semantic CWT credential Apple Pass template [#create-a-semantic-cwt-credential-apple-pass-template]
## Retrieve all Semantic CWT credential Apple Pass templates [#retrieve-all-semantic-cwt-credential-apple-pass-templates]
## Retrieve a Semantic CWT credential Apple Pass template [#retrieve-a-semantic-cwt-credential-apple-pass-template]
## Update a Semantic CWT credential Apple Pass template [#update-a-semantic-cwt-credential-apple-pass-template]
## Delete a Semantic CWT credential Apple Pass template [#delete-a-semantic-cwt-credential-apple-pass-template]
# Google Pass templates
URL: /docs/issuance/direct-issuance-api-reference/semantic-cwt-google-pass-templates
## Create a Semantic CWT credential Google Pass template [#create-a-semantic-cwt-credential-google-pass-template]
## Retrieve all Semantic CWT credential Google Pass templates [#retrieve-all-semantic-cwt-credential-google-pass-templates]
## Retrieve a Semantic CWT credential Google Pass template [#retrieve-a-semantic-cwt-credential-google-pass-template]
## Update a Semantic CWT credential Google Pass template [#update-a-semantic-cwt-credential-google-pass-template]
## Delete a Semantic CWT credential Google Pass template [#delete-a-semantic-cwt-credential-google-pass-template]
# Issuance
URL: /docs/issuance/direct-issuance-api-reference/semantic-cwt-issuance
## Sign a Semantic CWT credential [#sign-a-semantic-cwt-credential]
## Format a Semantic CWT credential as a QR code [#format-a-semantic-cwt-credential-as-a-qr-code]
## Format a Semantic CWT credential as a PDF [#format-a-semantic-cwt-credential-as-a-pdf]
## Format a Semantic CWT credential as an Apple Pass [#format-a-semantic-cwt-credential-as-an-apple-pass]
## Format a Semantic CWT credential as a Google Pass [#format-a-semantic-cwt-credential-as-a-google-pass]
# PDF templates
URL: /docs/issuance/direct-issuance-api-reference/semantic-cwt-pdf-templates
## Create a Semantic CWT credential PDF template [#create-a-semantic-cwt-credential-pdf-template]
## Retrieve all Semantic CWT credential PDF templates [#retrieve-all-semantic-cwt-credential-pdf-templates]
## Retrieve a Semantic CWT credential PDF template [#retrieve-a-semantic-cwt-credential-pdf-template]
## Update a Semantic CWT credential PDF template [#update-a-semantic-cwt-credential-pdf-template]
## Delete a Semantic CWT credential PDF template [#delete-a-semantic-cwt-credential-pdf-template]
# API Reference
URL: /docs/issuance/pre-authorized-code/api-reference
## Create Credential Offer [#create-credential-offer]
## Delete Credential Offer [#delete-credential-offer]
## Issue a verifiable credential [#issue-a-verifiable-credential]
## Retrieve issuer metadata [#retrieve-issuer-metadata]
# OID4VCI Pre-authorized Code flow journey pattern
URL: /docs/issuance/pre-authorized-code/journey-pattern
This journey pattern is used to issue credentials of different formats to a holder via the OID4VCI
[Pre-authorized Code flow](/docs/issuance/pre-authorized-code/overview) protocol.
## Overview [#overview]
* **Issuance channel**: Remote, Unsupervised
* **Device/s**: On-device / Cross-device / in-person
* **Formats**: mDocs
* **Information assurance level**: High
* **Identity assurance level**: High (depends on the mechanism used by the issuer to authenticate
the holder prior to sharing a credential offer)
## Journey flow [#journey-flow]
## Architecture [#architecture]
### Logging into a provider's portal [#logging-into-a-providers-portal]
The underlying architecture assumes that the Issuer can reliably confirm the user's identity before
issuing the credential offer. This enables seamless, low-friction issuance experiences while
preserving trust and security.
### Scanning the QR code [#scanning-the-qr-code]
The QR code used to initiate the issuance workflow is generated by the Issuer, while the Holder
controls when to scan it using their digital wallet. Scanning the QR code triggers the credential
issuance process.
### Credential offer [#credential-offer]
Once the QR code is scanned it will result in the wallet displaying the credential offer that was
created by the Issuer using MATTR VII issuance capabilities. When the QR code is scanned, the
Holder’s digital wallet initiates the credential issuance workflow and displays the credential offer
prepared by the Issuer using MATTR VII's issuance capabilities.
The offer outlines the credential formats to be issued and specifies the claims included in each
credential. In a pre-authorized flow, the Issuer can gather information about the intended Holder in
advance—since the offer is created for a known user—allowing the Issuer to tailor the credential
with specific, user-relevant claims.
Digital trust service capabilities enable creating and maintaining policies that define what
Issuers can be trusted and what credential types they are allowed to issue.
This introduces an additional level of trust to interactions within the trust network, making it
easier for Samantha to decide whether or not she wishes to claim a credential from this Issuer.
### Obtaining a binding attribute [#obtaining-a-binding-attribute]
The OpenID Credential Provisioning component commences the credential issuance flow by obtaining
a unique binding attribute from the requesting device/wallet. This happens when the user accepts the
credential offer.
The binding attribute is carried through the proceeding steps to bind the intended credential holder
and the data.
### Transaction code [#transaction-code]
To enhance the security of the pre-authorized issuance flow, the Issuer may optionally require the
Holder to provide a transaction code before the credential can be claimed. This code is generated by
the Issuer as part of the credential offer and is uniquely associated with that offer.
The Issuer sends the transaction code to the user through a secure, alternative communication
channel such as email or SMS. When the user initiates the issuance process by scanning the QR code,
they are prompted by the wallet to enter the transaction code. The credential can only be issued if
the correct code is supplied.
This optional verification step strengthens the assurance that the credential is issued to the
intended recipient, helping to prevent unauthorized access in scenarios where the credential offer
may have been intercepted or misdirected.
### Credential issuance [#credential-issuance]
The information then gets passed back through the OpenID Credential Provisioning component to
map against an established vocabulary, and express the intended context around each piece of
information it holds.
The mapped data is then passed to the Credential Generation component which formats, binds and
signs the data into a credential that is ready to be sent to the requesting wallet/device.
### Credential management [#credential-management]
Digital wallets can be used to manage the acceptance and secure storage of the credential on the
Holder’s device upon completion of the credential issuance flow. This can be achieved by wallets
built with our MATTR Pi Wallet SDK or branded MATTR GO Hold applications.
# OID4VCI Pre-authorized Code flow
URL: /docs/issuance/pre-authorized-code/overview
## Overview [#overview]
The Pre-authorized Code flow is a streamlined, user-friendly process where the credential recipient
(usually a wallet) is issued a pre-authorized code by the issuer (e.g., a government or
organization). This code can be used to obtain a credential without requiring the user to go through
the full authentication process again.
In the pre-authorized code flow, the issuer provides a pre-authorized code directly to the wallet.
The wallet uses this code to obtain an access token, which it then uses to request the issuance of a
credential.
This flow is ideal for scenarios where the user has already been authenticated and authorized, and
the issuer wants to simplify the credential issuance process. It is particularly useful for
situations where the user needs to obtain multiple credentials or where the issuance process needs
to be expedited.
The Pre-authorized Code flow is only supported for [mDocs](/docs/concepts/mdocs).
## Workflow [#workflow]
The following diagram depicts the OID4VCI Pre-authorized Code flow:
### Issuer preparation [#issuer-preparation]
The issuer prepares the credential issuance by authenticating the user and gathering claims about
them. This step is performed outside of the OID4VCI workflow and is not performed using MATTR
capabilities. The issuer can use any method to authenticate the user, such as a login page or a
custom implementation. The issuer then passes the user claims to MATTR VII, which will be used later
in the credential issuance process.
### Creating a Credential offer [#creating-a-credential-offer]
Once the issuer is ready, they initiate the creation of a
[credential offer](/docs/issuance/credential-offer/overview) by making a request that specifies:
* The [credential configuration](/docs/issuance/credential-configuration/overview) to use for
issuance.
* The user claims to be included in the credential.
* The credential’s expiry date.
* Whether a transaction code is required for claiming the credential.
MATTR VII will then create a credential offer that includes a pre-authorized code, which is used to
issue the credential. This code is a unique identifier that allows the wallet to claim the
credential without requiring the user to go through the full authentication process again.
The MATTR VII response will include a URI that can be used by the wallet to accept the credential
offer and request the credential.
### Sharing a Credential offer [#sharing-a-credential-offer]
The issuer can now share the credential offer with the intended holder. This is done by sending the
credential offer URI to the wallet. The URI can be shared in various ways, such as a QR code, a deep
link, or a push notification. The issuer can also choose to share the transaction code separately if
it is required for claiming the credential.
Refer to [Claiming credential offers](/docs/issuance/credential-offer/overview#claiming-credential-offers) for more details on how to adjust the offer URI to use specific schemes and the resulting user experience.
### Accepting a Credential offer [#accepting-a-credential-offer]
After receiving the credential offer URI, the wallet uses it to retrieve required metadata from the `.well-known` endpoints:
```http title="Issuer Metadata Endpoint"
https://{your_tenant_url}/.well-known/openid-credential-issuer
```
```http title="Authorization Server Metadata Endpoint"
https://{your_tenant_url}/.well-known/oauth-authorization-server
```
As required by the OID4VCI specification, these endpoints must be publicly accessible. They provide the information necessary to initiate the issuance process, including the issuer’s details, the types of credentials available, and the relevant endpoints and supported parameters used throughout the workflow.
If the credential offer includes a transaction code requirement, the wallet prompts the user to enter the code before proceeding. The transaction code is validated by the issuer when the wallet calls the token endpoint as part of the token exchange. If an incorrect transaction code is entered three times, the credential offer is permanently invalidated and the wallet can no longer claim the credential.
The wallet then calls the issuer’s token endpoint, exchanging the pre-authorized code (and, where required, the transaction code) for an access token. This token endpoint is hosted by MATTR VII and is accessible only when a valid pre-authorized code is provided.
**Single-use offers**: Pre-authorized Code flow offers are designed for a specific user who has already been authenticated out-of-band. Once the offer is successfully claimed and the credential is issued, the pre-authorized code is consumed and cannot be reused. Any subsequent attempts to claim the same offer will fail, even by the same user. This is a security measure to prevent unauthorized credential duplication.
If you need to issue another credential to the same user, you must generate a new credential offer.
Finally, the wallet uses the returned access token to call the issuer’s credential issuance endpoint and request the credential. This endpoint, also hosted by MATTR VII, is accessible only when a valid access token is included in the request.
### Formatting and Signing the credential [#formatting-and-signing-the-credential]
When the wallet requests the credential, MATTR VII takes the data provided by the issuer and formats
it into a digital credential. This credential is then cryptographically signed. The process uses a
[credential configuration](/docs/issuance/credential-configuration/overview) specified in the
credential offer, which acts as a template for the issued credential. The credential configuration
defines how claims are mapped, the credential’s appearance in the wallet, its expiration, and
whether it can be revoked.
### Delivering the signed credential [#delivering-the-signed-credential]
Finally, the signed credential is delivered directly to the holder’s digital wallet and can be
presented to verifying parties upon request.
## Configuration [#configuration]
The OID4VCI workflow makes use of different components that can be configured either using direct
API requests to a MATTR VII tenant or via the
[MATTR Portal](/docs/platform-management/portal), which offers an interface layer on
top of the APIs.
* Issuer identity management (*required*): Set up and manage the identifiers that represent your
issuer and are essential for establishing the issuer's identity in the credential issuance
process. As the Pre-authorized Code only supports mDocs, this means creating an
[IACA](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca).
* [Credential configuration](/docs/issuance/credential-configuration/overview) (*required*): Add your
credential types, branding, claims, and other metadata. You can also mix and match where claims
for the issued credentials come from - an authentication provider or a claims source.
* [Credential offer](/docs/issuance/credential-offer/overview) (*required*): This is the main
component of the OID4VCI Pre-authorized Code flow. It contains the credential configuration
identifier, the pre-authorized code, and other metadata. The credential offer is used to initiate
the issuance process and includes all necessary information for the wallet to request and claim
the credential.
* [Claims source](/docs/issuance/claims-source/overview) (*optional*): While the intent of the
Pre-authorized Code flow is for the issuer to provide all the required claims when creating the
credential offer, there may be cases where additional claims are needed. If you have additional
user information stored in a separate database or service, add a claims source to fetch claims
directly from a compatible standalone system and use these claims when issuing credentials.
# OID4VCI Pre-authorized Code flow quickstart guide
URL: /docs/issuance/pre-authorized-code/quickstart
This quickstart is for evaluating MATTR’s OID4VCI Pre-authorized Code flow issuance capabilities. In about 10-15 minutes you will configure an OID4VCI Pre-authorized Code flow in the MATTR Portal, generate a credential offer, and claim an mDoc into the GO Hold example app.
**Estimated time: 10-15 minutes.**
Use this guide as a fast evaluation path to see the flow working end-to-end. For detailed information and API examples, explore the
[tutorial](/docs/issuance/pre-authorized-code/tutorial) and reference documentation.
## User experience [#user-experience]
In this quickstart you will perform this exact flow yourself using the MATTR Portal and the GO Hold example app:
1. User scans a QR code from an issuer.
2. The wallet displays what credential is being offered.
3. The user accepts the offer and provides a transaction code.
4. The credential is immediately issued to the wallet.
In this flow there is no user authentication step, making it a great option for low-risk credentials or use cases where the user has already authenticated through another channel (e.g., a mobile app).
## Prerequisites [#prerequisites]
* MATTR VII tenant access via the [MATTR Portal](https://portal.mattr.global/). Apply for access [here](/docs/resources/get-started).
* Install the **MATTR GO Hold example app** for [iOS](https://apps.apple.com/app/mattr-wallet/id1518660243) or [Android](https://play.google.com/store/apps/details?id=global.mattr.wallet).
## Steps [#steps]
### Create issuer certificate (2 minutes) [#create-issuer-certificate-2-minutes]
This allows your tenant to act as an issuing authority for mDocs in this demo. This step is only required if you haven't already set up an issuer certificate for your tenant. If you already have an active IACA, skip to the next step.
1. Log into the [MATTR Portal](https://portal.mattr.global/).
2. Switch to your tenant if you have access to multiple tenants, or [create a new tenant](/docs/platform-management/portal#creating-a-tenant) if needed.
3. Expand **Platform Management**.
4. Select **Certificates**.
5. Select **Create new**.
6. Select **IACA - Issuing Authority Certificate Authority** as the type.
7. Select **MATTR managed** as the management method.
8. Select **Create**.
9. Set **Status** to **Active**.
10. Select **Update** to activate the certificate.
### Create mDoc credential configuration (3 minutes) [#create-mdoc-credential-configuration-3-minutes]
In this quickstart you’ll use a simple credential configuration so you can issue a credential without integrating any external data sources:
1. Expand **Credential Issuance**.
2. Select **mDocs**.
3. Select **Create new**.
4. Enter a **Name** (e.g., "My First Credential").
5. Enter a **Description** (e.g., "Claimed via Pre-authorized Code flow").
6. Enter a **Credential type** (e.g., `com.example.preauthcredential`).
7. Paste the following JSON into **Claim mappings**:
```json title="Claim mappings object"
{
"com.example.personaldetails.1": {
"name": {
"defaultValue": "Emma Tasma",
"type": "string"
},
"email": {
"defaultValue": "emma.tasma@example.com",
"type": "string"
}
}
}
```
8. Enter "1" in the **Months** field under **Validity for**.
9. Select **Create**.
### Generate a credential offer (2 minutes) [#generate-a-credential-offer-2-minutes]
This creates the OID4VCI offer that wallets can use to start the Pre-authorized Code flow.
1. Expand **Credential Issuance**.
2. Select **Credential offer**.
3. Select **Pre-authorized code flow** as the workflow.
4. Select the **Select** button.
5. Check the checkbox next to your credential configuration.
6. Select **Apply**.
7. Select **Generate**.
8. Copy the Transaction code that is displayed on the screen.\
In production deployments you will need to provide it to the user through a different channel
(e.g., email, SMS).
9. Download the displayed QR code (or just leave it on the screen for scanning in the next step).
### Claim the credential (2 minutes) [#claim-the-credential-2-minutes]
Now use the GO Hold example wallet to experience the end-to-end flow from QR scan to credential in the wallet:
1. Open the [**GO Hold example app**](/docs/holding/go-hold/getting-started).
2. Select **Share** on the home screen.
3. Select **Respond or Collect** (You may need to allow the app to access your camera).
4. Scan the QR code you generated in the previous step.
5. Review the credential offer and select **Proceed**.
6. Enter the Transaction code you copied earlier.
7. The new credential should now be visible in the GO Hold example app.
Behind the scenes, MATTR handled the OID4VCI Pre-authorized Code flow, including validating the Transaction code and credential offer details and issuing the mDoc to your GO Hold example app.
Congratulations! You've successfully configured an OID4VCI Pre-authorized Code flow and issued an mDoc to a digital wallet using only MATTR Portal configuration and the GO Hold example app.
## Next steps [#next-steps]
* For your evaluation:
* Note how long this quickstart took and any friction you encountered.
* Review the credential configuration and consider how it would map to your real data.
* Explore the [OID4VCI Pre-authorized Code flow tutorial](/docs/issuance/pre-authorized-code/tutorial) for detailed instructions and explanations.
* Try the [OID4VCI Authorization Code flow](/docs/issuance/authorization-code/quickstart) for a different issuance workflow with user authentication.
# Learn how to configure an OID4VCI Pre-authorized Code flow to issue an mDoc into a digital wallet
URL: /docs/issuance/pre-authorized-code/tutorial
## Introduction [#introduction]
The
[OID4VCI specification](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html)
is an open standard developed by the OpenID Foundation. It leverages the OpenID protocol to support
verifiable credentials issuance and management.
In this tutorial we will configure an
[OID4VCI Pre-authorized Code flow](/docs/issuance/pre-authorized-code/overview) and use it to
issue an [mDoc](/docs/concepts/mdocs) into a
[MATTR GO Hold example app](/docs/holding/go-hold/getting-started), showing each step both through the MATTR Portal and via the equivalent API calls.
## User experience [#user-experience]
This is the user experience you will build in this tutorial:
1. The user launches their [GO Hold example app](/docs/holding/go-hold/getting-started) and
scans a QR code received from an issuer.
2. The GO Hold example app displays what credential is being offered and by what issuer.
3. Once the user accepts the offer, they are required to provide a transaction code. This code is
provided to the user by the issuer through a different channel (e.g. email, SMS, printed on
paper).
4. Once the transaction code is provided, the credential is immediately issued to the user's GO Hold
example app. They can now view the credential and present it for verification.
**UX considerations**
* *No prior authentication required*: Users are not required to authenticate before the credential
is issued. This simplifies the process but assumes the issuer has already verified the user prior
to providing the credential offer (e.g., during login to a wallet application or service portal).
* *Implicit credential acceptance*: The user is not technically required to explicitly accept the
credential offer. Depending on how the flow is implemented with wallet providers, the credential
can be issued immediately upon scanning the QR code.
* *Optional transaction code*: The use of a transaction code is optional and can be omitted if not
required by the issuer. In this tutorial, we include it to demonstrate how an additional layer of
security can be added to the issuance workflow.
## Prerequisites [#prerequisites]
* Complete the [sign up form](/docs/resources/get-started) to get trial access to MATTR VII and
the MATTR Portal, and then [Create a tenant](/docs/platform-management/portal#creating-a-tenant).
* Install the **MATTR GO Hold example app** by following the
[getting started guide](/docs/holding/go-hold/getting-started).
We recommend using the MATTR VII [Postman
collection](/docs/api-reference#postman-collection)
in this tutorial. While this isn't an explicit prerequisite it can really
speed things up.
## Tutorial overview [#tutorial-overview]
To build this user experience, the current tutorial comprises the following steps:
1. [Create Issuer certificates](#create-issuer-certificates): Required to sign mDocs.
2. [Create an mDocs credentials configuration](#create-a-mattr-vii-mdocs-credentials-configuration):
Controls the content and branding of issued Credentials.
3. [Create and share a Credential offer](#create-a-credential-offer): Used by digital wallets to
trigger the issuance workflow.
4. [Claim the credential as the holder](#use-the-mattr-go-example-app-to-claim-the-credential): Use
your GO Hold example app to claim the offered Credential.
## Tutorial steps [#tutorial-steps]
### Create issuer certificates [#create-issuer-certificates]
In this tutorial you are going to issue an [mDoc](/docs/concepts/mdocs), so you need to have valid
[IACA](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca).
1. Log into the [MATTR Portal](https://portal.mattr.global/).
2. In the navigation panel on the left-hand side, expand the **Platform Management** menu.
3. Select **Certificates**.
4. Select the **Create new** button.
5. Use the *Type* radio button to select **IACA - Issuing Authority Certificate Authority**.
6. Use the *Management method* radio button to select **MATTR managed**.
7. Use the *Country* dropdown list to select an issuing country.
8. Select the **Create** button to create the IACA certificate.\
The IACA is created as *inactive* by default.
9. Use the *Status* radio button to select **Active**.
10. Select the **Update** button to activate the IACA certificate.
**Step 1: Obtain a MATTR VII access token**
All of the MATTR VII endpoints you will use in this tutorial
are protected, so you will first need to use your [tenant details](/docs/platform-management/portal#getting-started) to make a request of the following
structure and obtain an access token:
```http title="Request"
POST https://{auth_server}/oauth/token
```
* `auth_server` : Replace with the `auth_url` value from your tenant details.
```json title="Request body"
{
"client_id": "F5qae****************************",
"client_secret": "Wzc8J**********************************************************",
"audience": "learn.vii.au01.mattr.global",
"grant_type": "client_credentials"
}
```
* Replace `client_id`, `client_secret` and `audience` with your own tenant details.
* Keep `grant_type` as `client_credentials`.
*Response*
```json title="Response body"
{
"access_token": "eyJhb********************************************************************",
"expires_in": 14400,
"token_type": "Bearer"
}
```
Use the returned `access_token` value as a bearer token for all requests to your MATTR VII tenant in
the next steps of this tutorial.
You will need to obtain a new access token whenever it expires. We recommend
using our [Postman
collection](/docs/api-reference#postman-collection)
to interact with your MATTR VII tenant. This collection includes a pre-request
script to obtain an access token when it is missing or expired, as well as
pre-configured templates for all available requests.
**Step 2: Create the IACA certificate**
1. Make the following request to your MATTR VII tenant to create an
[IACA](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca):
```http filename:"Request"
POST /v2/credentials/mobile/iacas
```
The response will include an `id` element which you will use in the next step.
2. Make the following request to activate the IACA you just created:
```http filename:"Request"
PUT /v2/credentials/mobile/iacas/{iacaId}
```
* `iacaId`: Replace with the `id` element returned in the response when you created the IACA in the
previous steps.
```http filename:"Request body"
{
"active": true
}
```
Your IACA is now active and can be used to sign mDocs.
### Create a MATTR VII mDocs credential configuration [#create-a-mattr-vii-mdocs-credential-configuration]
1. In the navigation panel on the left-hand side, expand the **Credential Issuance** menu.
2. Select **mDocs**.
3. Select the **Create new** button.
4. In the *Name* text box, enter a clear and descriptive title that will appear on the credential in
the wallet, for example "My First Pre-Auth Credential".
5. In the *Description* text box, enter a clear and descriptive description that will appear on the
credential in the wallet, for example "For High Assurance Interactions".
6. In the *Credential type* text box, enter a unique identifier for the credential type, for example
`com.example.myfirstpreauthcredential`.
7. Copy and paste the following JSON into the *Claim mappings* text box:
```json title="Claim mappings"
{
"com.example.personaldetails.1": {
"name": {
"mapFrom": "claims.name",
"type": "string"
},
"email": {
"mapFrom": "claims.email",
"type": "string"
}
}
}
```
8. Use the *Include status* dropdown list to select **Enable**. This will enable support for changing the revocation status of the issued credentials.
9. Enter "1" in the *Months* text box in the *Validity for* panel to set the credential expiration
period.
10. Select the **Create** button to create the credential configuration.
Make the following request to create a simple
[mDoc credentials configuration](/docs/issuance/credential-configuration/api-reference/mdocs#create-an-mdocs-credential-configuration)
that includes the holder's name and e-mail:
```http title="Request"
POST /v2/credentials/mobile/configurations
```
```json title="Request body"
{
"type": "com.example.myfirstpreauthcredential",
"expiresIn": {
"months": 1
},
"claimMappings": {
"com.example.personaldetails.1": {
"name": {
"mapFrom": "claims.name",
"type": "string"
},
"email": {
"mapFrom": "claims.email",
"type": "string"
}
}
},
"branding": {
"name": "My First Pre-Auth Credential",
"description": "For High Assurance Interactions",
"backgroundColor": "#a2d82dff"
},
"includeStatus": true
}
```
The response will include an `id` element. You will use it to create a Credential offer in the next
step.
### Create a Credential offer [#create-a-credential-offer]
You now have all the pieces in place and can wrap them all together to generate a
[Credential offer](/docs/issuance/credential-offer/overview) and share it with the intended holder so
that they know what credentials are being offered and by whom.
Creating pre-authorized code flow credential offers in the MATTR Portal is for testing purposes only. The populated user ID is generated by the MATTR Portal and cannot be changed to another user later.
1. In the navigation panel on the left-hand side, expand the **Credential Issuance** menu.
2. Select **Credential offer**.
3. Use the *Workflow* radio button to select **Pre-authorized code flow**.
4. Select the **Select** button.
5. Check the checkbox next to the credential configuration you created in the previous step.
6. Select the **Apply** button.
7. In the *Claims* panel, click the **Add new** button and add the following claims:
* Attribute: `name`, Type: `String`, Value: `John Doe`
* Attribute: `email`, Type: `String`, Value: `john.doe@example.com`\
This is an example for demonstration purposes only. In production deployments, these claims would typically be populated dynamically from a data source or provided programmatically when creating the credential offer.
8. Select the **Generate** button.
9. Copy the Transaction code that is displayed on the screen.\
In production deployments you will need to provide it to the user through a different channel
(e.g., email, SMS).
10. Download the displayed QR code.\
This QR code will be used by the holder to claim the credential.
Make the following request to
[generate a new Pre-authorized Code flow Credential offer](/docs/issuance/pre-authorized-code/api-reference#create-credential-offer):
```http title="Request"
POST /v1/openid/offers/pre-authorized
```
```json title="Request body"
{
"credentials": ["707e920a-f342-443b-ae24-6946b7b5033e"],
"transactionCodeConfiguration": {
"inputMode": "numeric",
"description": "Please enter the one-time code that was sent to you via email."
},
"claims": {
"name": "John Doe",
"email": "john.doe@example.com"
},
"expiresIn": {
"minutes": 10
}
}
```
* `credentials`: Populate the array with the `id` element returned in the response when you created
an mDocs credentials configuration in the previous step.
In this example, the claims are included directly in the credential offer.
This is allowed in the Pre-authorized Code flow because the user's identity
has already been verified. You can provide these claims programmatically when
creating the credential offer, or you can reference a [Claims
source](/docs/issuance/claims-source/overview) in your credential
configuration to dynamically populate the claims in the issued credential.
*Response*
```json title="Response body"
{
"id": "e6e5e43c-8053-464a-aca4-ca43da765c97",
"uri": "openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Flearn.vii.au01.mattr.global%22%2C%22credentials%22%3A%5B%22b7b380be-1467-446b-8683-1d131e6532be%22%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%229K3N9UPQD-Hs5f5-gPx0fRJEmb1XTZsWszzXL024pV0%22%2C%22tx_code%22%3A%7B%22length%22%3A6%2C%22input_mode%22%3A%22numeric%22%2C%22description%22%3A%22Please%20enter%20the%20one-time%20code%20that%20was%20sent%20to%20you%20via%20email.%22%7D%7D%7D%7D", // [!code focus]
"userId": "6e30dd69-c867-4279-afd3-e6619253b4a4",
"expiresAt": "2025-08-25T01:39:00.523Z",
"transactionCode": "763677" // [!code focus]
}
```
You will need to obtain two important elements from the response:
* `transactionCode` : This is the one-time code that the user will need to provide in order to claim
the credential. In production deployments you will need to provide it to the user through a
different channel (e.g., email, SMS, printed on paper).
* `uri` : This URI can be used by a digital wallet to trigger the OID4VCI workflow. Use one of the
following tools to convert the `uri` value to a QR code (make sure you use the `Plain text` option
where available) and save the generated QR code as an image file.
* [https://www.the-qrcode-generator.com/](https://www.the-qrcode-generator.com/)
* [http://goqr.me/api/](http://goqr.me/api/)
* [https://www.qr-code-generator.com/](https://www.qr-code-generator.com/)
MATTR is not affiliated with any of these service providers and cannot vouch
for their offerings.
### Use the MATTR GO Hold example app to claim the credential [#use-the-mattr-go-hold-example-app-to-claim-the-credential]
1. Open the GO hold example app.
2. Select **Scan**.
3. Scan the QR code generated in the previous step.
4. Review the credential offer and select **Accept**.
5. Provide the transaction code provided when you created the credential offer.
6. Follow the on-screen instructions to claim the credential.
Congratulations, you just configured an end-to-end
[OID4VCI Pre-authorized Code flow](/docs/issuance/pre-authorized-code/overview) to issue an mDoc
into a digital wallet!!!
## What's next? [#whats-next]
In this tutorial we have configured a basic
[OID4VCI Pre-authorized Code flow](/docs/issuance/pre-authorized-code/overview). However, you
can use MATTR VII to create more complex and rich issuance experience. Check out more resources on
MATTR Learn that will enable you to:
* Experiment with a different issuance flow by completing the
[OID4VCI Authorization Code flow](/docs/issuance/authorization-code/tutorial) tutorial.
* Configure a [Claims source](/docs/issuance/claims-source/tutorial) to retrieve data from
compatible data sources and use it in the issued credential.
* Apply branding to issued credentials as part of creating a
[Credential configuration](/docs/issuance/credential-configuration/overview).
# Credential Refresh
URL: /docs/issuance/refresh/overview
Credential refresh is offered as a closed beta preview feature and is not generally available yet. If you are interested in trying this feature, please [contact us](mailto:dev-support@mattr.global).
## Overview [#overview]
Credential Refresh enables a wallet to claim new instances of credentials without requiring the user to authenticate again.
This capability supports several use cases:
* **Validity extension**: Silently refresh credentials before they expire to extend their validity period.
* **Data synchronization**: Obtain credentials with the most up-to-date information as the issuer's data changes.
* **Improved user experience**: Eliminate repeated authentication flows for credential re-issuance.
Credential Refresh behavior depends on the holder implementation. For example, a silent refresh only occurs if the wallet’s authentication policy allows it without user interaction. Issuers should assess holder capabilities and security policies before enabling Credential Refresh.
## How it works [#how-it-works]
To understand how Credential Refresh works, you need to understand the orchestration steps that occur between a wallet and an issuer in OpenID for Verifiable Credential Issuance (OID4VCI):
1. The wallet calls the Token endpoint with an Authorization Code (provided with the credential offer in Pre-authorized Code flows or from the authentication provider in Authorization Code flows).
2. If the Authorization Code is valid, the issuer returns an access token.
3. The wallet uses this access token to call the Credential endpoint, which triggers the issuance workflow and returns the issued credential to the wallet.
### Enabling Credential Refresh [#enabling-credential-refresh]
MATTR VII issues a credential as refreshable when two conditions are met:
1. **The credential configuration enables refresh**: Set `refreshPolicy.enabled` to `true` in the credential configuration to define that issued credentials are refreshable.
2. **The wallet provides a DPoP token**: When calling the Token endpoint with their Authorization Code, the wallet must provide a [DPoP](https://datatracker.ietf.org/doc/html/rfc9449) (Demonstrating Proof-of-Possession) token.
DPoP is critical for credential refresh because it cryptographically binds tokens to the wallet's key material. This ensures that refresh tokens can only be used by the specific wallet that originally claimed the credential, preventing token theft and replay attacks.
### The refresh flow [#the-refresh-flow]
Once the above conditions are met:
1. When the wallet hits the token endpoint, the MATTR VII issuance tenant returns a refresh token alongside the issued credential. The refresh token is specific to the claiming wallet and credentials included in the credential configuration.
2. The wallet can later call the Token endpoint again with a DPoP token, but instead of providing an Authorization Code, they provide the refresh token received from the issuer.
In this scenario, the issuer would also return a new refresh token alongside the newly issued access token, allowing the wallet to continue refreshing in the future.
3. The issuer validates the combination of DPoP and refresh token, and issues a new credential based on the same credential configuration.
4. The new credential includes the most up-to-date information available based on:
* The claims mapping in the credential configuration.
* Validity dates.
* Any information stored in configured claim sources.
* In Authorization Code flows, any updated information from the authentication provider is not available during refresh.
* Any ephemeral claims passed during the original issuance flow would also not be available upon refresh.
Refresh tokens remain valid until revoked. However, if the credential configuration is changed to no longer support refresh, credential refresh would fail even if the refresh token has not been revoked.
### Integrity Protection [#integrity-protection]
To further protect the integrity of requests to endpoints that use DPoP proof tokens (such as the Token endpoint and Credential endpoint), MATTR VII supports validating the content digest of the HTTP request payload through the DPoP proof token.
When making requests to these endpoints, wallets can include an optional `htcd` (HTTP Content Digest) claim in their DPoP proof token. This claim provides cryptographic verification that the request payload has not been tampered with during transmission.
When the optional `htcd` claim is provided:
* The claim MUST contain the content digest value of the HTTP request payload.
* The value MUST align with the [HTTP Content-Digest Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Digest) format, i.e. `sha-256=:${base64(sha256(requestBody))}=:`
* If a `Content-Digest` header is also present in the HTTP request, MATTR VII will compare both values to ensure consistency.
The following is in an example of a DPoP proof token with the `htcd` claim:
```json title="DPoP Proof Token"
{
"typ":"dpop+jwt",
"alg":"ES256",
"jwk": {
"kty":"EC",
"x":"l8tFrhx-34tV3hRICRDY9zCkDlpBhF42UQUfWVAWBFs",
"y":"9VE4jf_Ok_o64zbTTlcuNJajHmt6v9TDVrU0CdvGRDA",
"crv":"P-256"
}
}
.
{
"jti":"-BwC3ESc6acc2lTc",
"htm":"POST",
"htu":"https://server.example.com/token",
"htcd": "sha-256=:47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=:",
"iat":1562262616
}
```
When MATTR VII receives a refresh request with a DPoP proof token containing the `htcd` claim, it will validate the content digest against the actual request payload. If there is a mismatch, the request will be rejected to ensure the integrity of the credential refresh process.
# Integrating mDocs revocation into your workflow
URL: /docs/issuance/revocation/guide
## Overview [#overview]
This guide walks you through integrating mDocs revocation into your issuance workflow. It covers
the end-to-end process of issuing revocable credentials, tracking them, and revoking them when
needed.
## Prerequisites [#prerequisites]
* An active MATTR VII tenant. If you don't have one, complete the
[sign up form](/docs/resources/get-started) and
[create a tenant](/docs/platform-management/portal#creating-a-tenant).
* Familiarity with the [Revocation overview](/docs/issuance/revocation/overview), which explains
the underlying concepts such as status lists, status values, and status list tokens.
* Familiarity with at least one OID4VCI issuance flow:
* [Pre-Authorized Code flow](/docs/issuance/pre-authorized-code/overview)
* [Authorization Code flow](/docs/issuance/authorization-code/overview)
## User journey [#user-journey]
The following diagram illustrates the revocation journey for issuers, holders, and verifiers:
1. **Issue a revocable credential**: The issuer's backend system creates a credential offer in
MATTR VII for an existing database record and stores the correlation data. The backend shares the
offer with the holder, who claims it. MATTR VII issues the credential with its status tracked in
a publicly available status list, and sends a webhook with the `credentialId` back to the
issuer's backend, which stores it against the database record.
2. **Database record changes**: The issuer's backend system detects a change in the database record
that requires revoking the associated credential (such as a permit expiration or license
suspension).
3. **Revoke the credential**: The issuer's backend retrieves the stored `credentialId` from the
database record and calls MATTR VII to update the credential status to `invalid`. MATTR VII
updates the status list.
4. **Holder checks credential status**: When the holder's wallet checks the credential status with
MATTR VII, it receives the updated status showing the credential is revoked.
5. **Verifier checks credential status**: When a verifier checks the revoked credential, MATTR VII reports the
revoked status, verification fails, and the verifier is informed of the revocation.
## Integrating revocation into your workflow [#integrating-revocation-into-your-workflow]
Integrating revocation into your workflow involves three main steps:
1. Enable revocation in your credential configuration to ensure issued credentials are tracked in a status list.
2. Track issued credentials by capturing their `credentialId` through webhooks or API queries.
3. Revoke credentials when necessary by updating their status to `invalid`.
## Step 1: Enable revocation in your credential configuration [#step-1-enable-revocation-in-your-credential-configuration]
Issued mDocs are not revocable by default. To issue revocable mDocs, you must manually adjust the credential configuration so that every credential issued with this
configuration will be added to a status list, enabling you to change its status at a later date.
Enabling revocation does not affect the issuance workflow or how the credential appears to
the holder. The only difference is that the issued mDoc will include a `status` reference in its
MSO payload, pointing to the status list managed by your tenant and the index of the credential within that list.
Enabling revocation can be performed via the Portal or API when creating or updating an mDoc credential configuration:
1. In the navigation panel, expand **Credential Issuance** and select **mDocs**.
2. Select an existing mDoc configuration.
3. Use the *Include status* radio button to select **Enable**.
4. **Save** the configuration.
When creating or updating an mDoc credential configuration, set `includeStatus` to `true` in the
request body:
```http title="Request"
POST /v2/credentials/mobile/configurations
```
```json title="Request body"
{
"type": "org.iso.18013.5.1.mDL",
"includeStatus": true, // [!code focus]
"claimMappings": {
// Your claim mappings
}
// Other configuration fields
}
```
See the
[API reference](/docs/api-reference/platform/mdoc-credentials-configuration/createMobileCredentialConfiguration)
for the full request schema.
## Step 2: Track revocable credentials [#step-2-track-revocable-credentials]
To revoke a credential, you will need its `credentialId`. This identifier is generated when the
credential is issued, so you need a strategy to capture and store it. The recommended approach is to
configure a [webhook](/docs/platform-management/webhooks-overview) that notifies your backend when a
credential is issued.
### Configure a webhook [#configure-a-webhook]
Set up a webhook to receive notifications whenever a credential is claimed. The webhook payload
includes the `credentialId` and other metadata you can use to associate the credential with a user or
record in your system.
This can be done via the Portal or API:
1. In the navigation panel, expand **Platform Management** and select **Webhooks**.
2. Click **Create new**.
3. Select the **OpenID credential issued - Summary** event.
4. Enter the HTTPS endpoint URL where you want to receive the webhook events.
5. Select **Create**.
Make a request to
[create a webhook](/docs/api-reference/platform/webhooks/createWebhook):
```http title="Request"
POST /v1/webhooks
```
```json title="Request body"
{
"events": ["OpenIdCredentialIssuedSummary"],
"url": "https://your-backend.example.com/webhooks/credential-issued"
}
```
Webhook events require verification and proper handling to ensure security and reliability. The
steps above focus on the webhook configuration relevant to revocation workflows. For complete
details on verifying webhook signatures, handling retries, and implementing idempotent handlers,
refer to the [Webhooks overview](/docs/platform-management/webhooks-overview),
[Webhooks guide](/docs/platform-management/webhooks-guide), and
[Webhooks tutorial](/docs/platform-management/webhooks-tutorial).
### Handle the webhook payload [#handle-the-webhook-payload]
When a credential is issued, MATTR VII sends a webhook notification with the following structure:
```json title="OpenIdCredentialIssuedSummary payload"
{
"format": "mso_mdoc",
"userId": "7382276d-ef75-4d17-8fb0-1d3aec4647ab", // [!code focus]
"credentialProfile": "mobile",
"credentialConfigurationId": "1d8c7ad7-84ce-4519-8365-7af986e4ee0e",
"credentialOfferId": "3b4f5cf6-4069-4c51-bbed-8165b4f9a889", // [!code focus]
"credentialId": "9613ac5e-a0ba-4512-ba0b-90e91b2744bc", // [!code focus]
"userClaims": { // [!code focus]
"externalUserId": "student-12345" // [!code focus]
} // [!code focus]
}
```
Key fields for revocation tracking:
* `credentialId`: The unique identifier of the issued credential, which you will use to revoke this credential later.
* `credentialOfferId`: The identifier of the credential offer that initiated this issuance. Only
present for Pre-Authorized Code flows.
* `userId`: The MATTR VII User ID associated with this credential.
* `userClaims`: Claims persisted on the MATTR VII user object, which can include external identifiers
you set up during the issuance workflow.
### Correlate credentials with your records [#correlate-credentials-with-your-records]
To revoke a credential, you need to identify which credential to revoke. This requires correlating
an issued credential with either:
* **A specific user**: When you need to revoke all credentials issued to a particular individual
(for example, when an employee leaves your organization).
* **A specific record**: When you need to revoke a credential tied to a particular data record in
your system (for example, when a license expires or a qualification changes).
Accurate correlation ensures you revoke the correct credential. The approach differs based on the
issuance flow you use and what you are correlating to (user vs record).
#### Pre-Authorized Code flow [#pre-authorized-code-flow]
A Pre-Authorized Code credential offer can only be claimed **once**. Once the holder successfully
claims the credential, the pre-authorized code is consumed and the offer becomes invalid. This
one-to-one relationship between offer and credential makes correlation straightforward.
In Pre-Authorized Code flows, you control the credential offer creation and can choose between two
correlation approaches depending on what you need to associate the credential with.
##### Correlate by offer ID (for database records) [#correlate-by-offer-id-for-database-records]
Use this approach when you need to associate credentials with specific database records (such as work
permits, licenses, student enrollments, or employee records). Each credential represents a specific
record in your system, and you need to revoke the credential when that record changes.
**The typical workflow:**
1. **Create a credential offer based on an existing database record**: When a record in your system
requires a credential (for example, a newly approved work permit or an issued driver's license),
use MATTR VII to create a credential offer for that record.
2. **Capture and store the offer ID**: The response includes an offer `id`. Store this identifier
against the original record in your database.
3. **Handle the credential issuance webhook**: When the credential is claimed, your webhook handler
receives an event that includes both the `credentialOfferId` and the `credentialId`. Use the
`credentialOfferId` to locate the relevant record in your database, then store the `credentialId`
against that same record.
4. **Revoke when the record changes**: When changes to your database require revoking the
credential (such as a permit expiration, license suspension, or record invalidation), retrieve
the stored `credentialId` from your database record and use it to revoke the credential.
**Create a credential offer for a database record**
When you create a Pre-Authorized Code credential offer, the response includes an `id` field. Store
this identifier alongside the corresponding record in your system.
```http title="Request"
POST /v1/openid/offers/pre-authorized
```
```json title="Request body"
{
"credentials": [
"946c4d4a-289b-4d14-8082-41b6bf749c35"
],
"claims": {},
"expiresIn": {
"minutes": 5,
"seconds": 0
}
}
```
```json title="Response"
{
"id": "8241400f-de3b-42c5-ad7c-8a380039e796", // [!code focus]
"uri": "openid-credential-offer://..."
}
```
Store the `id` value and map it to the relevant record in your database.
**Handle the webhook and store the credential ID**
When your webhook handler receives the `OpenIdCredentialIssuedSummary` event, use the
`credentialOfferId` field to locate the original record in your database. Then store the
`credentialId` against that record so you can revoke the credential if the underlying record changes
in the future.
```json title="Webhook payload"
{
"credentialOfferId": "8241400f-de3b-42c5-ad7c-8a380039e796", // [!code focus]
"credentialId": "9613ac5e-a0ba-4512-ba0b-90e91b2744bc", // [!code focus]
"userId": "19bf8183-a9dc-41cd-9336-1f5d19f1ae3d",
"userClaims": {}
}
```
##### Correlate by external user ID (for users) [#correlate-by-external-user-id-for-users]
Use this approach when you need to associate credentials with specific users in your system. Each
credential is tied to a user, and you need to revoke all credentials when a user's status changes
(such as when an employee leaves or a membership expires).
**The typical workflow:**
1. **Ensure the user exists in MATTR VII**: Before creating the credential offer,
[search for or create a user](/docs/issuance/users/guide#before-issuing-the-credential) in
MATTR VII. Store an external identifier (such as your system's user ID) in the user's `claims`
object.
2. **Create a credential offer associated with the user**: Include the MATTR VII `userId` in the
credential offer request. This associates the offer with the user.
3. **Handle the credential issuance webhook**: When the credential is claimed, your webhook handler
receives an event that includes the `credentialId` and `userClaims` (which contain your external
user identifier). Use the `userClaims.externalUserId` to locate the user in your system, then
store the `credentialId` against that user.
4. **Revoke when the user's status changes**: When a user's status changes and requires credential
revocation (such as employment termination or membership expiration), retrieve all stored
`credentialId` values for that user and revoke them.
**Create a user with an external identifier**
Before creating the credential offer, ensure a MATTR VII user exists with your external user
identifier stored in their claims:
```http title="Request"
POST /v1/users
```
```json title="Request body"
{
"claims": {
"externalUserId": "employee-12345" // [!code focus]
}
}
```
```json title="Response"
{
"id": "19bf8183-a9dc-41cd-9336-1f5d19f1ae3d", // [!code focus]
"claims": {
"externalUserId": "employee-12345"
}
}
```
Store the MATTR VII user `id` for use in the next step.
**Create a credential offer associated with the user**
When creating the credential offer, include the `userId` to associate it with the user:
```http title="Request"
POST /v1/openid/offers/pre-authorized
```
```json title="Request body"
{
"credentials": [
"946c4d4a-289b-4d14-8082-41b6bf749c35"
],
"userId": "19bf8183-a9dc-41cd-9336-1f5d19f1ae3d", // [!code focus]
"claims": {},
"expiresIn": {
"minutes": 5,
"seconds": 0
}
}
```
**Handle the webhook and store the credential ID**
When your webhook handler receives the `OpenIdCredentialIssuedSummary` event, use the
`userClaims.externalUserId` field to locate the user in your database. Then store the `credentialId`
against that user so you can revoke their credentials if their status changes.
```json title="Webhook payload"
{
"credentialOfferId": "8241400f-de3b-42c5-ad7c-8a380039e796",
"credentialId": "9613ac5e-a0ba-4512-ba0b-90e91b2744bc", // [!code focus]
"userId": "19bf8183-a9dc-41cd-9336-1f5d19f1ae3d",
"userClaims": { // [!code focus]
"externalUserId": "employee-12345" // [!code focus]
} // [!code focus]
}
```
You can also retrieve all credentials issued to a specific user via the
[Retrieve all user credentials data](/docs/api-reference/platform/users/getUserCredentials)
endpoint, which returns credential metadata including the `credentialId`, `status`, and `offerId`.
#### Authorization Code flow [#authorization-code-flow]
In Authorization Code flows, you don't know who will claim the credential until they authenticate.
The user's identity is determined by the authentication provider during the issuance process.
An Authorization Code credential offer can be claimed **multiple times** by **different users**.
Each user authenticates independently and receives their own credential. This means you cannot
associate the offer with a specific credential or user in advance. Instead, you must correlate the
issued credential with your records **after** the credential is issued, using information from the
webhook event.
In Authorization Code flows, correlation always happens post-issuance. You can choose between two
approaches depending on what you need to associate the credential with.
##### Correlate to database records via custom claims [#correlate-to-database-records-via-custom-claims]
Use this approach when you need to associate credentials with specific database records rather than
users directly. For example, when a credential represents a license or permit that exists as a
separate record in your system, and multiple people might authenticate to claim the same type of
credential for their own individual records.
In this scenario, you can use custom claims or interaction hooks to include a database record
identifier during the authentication flow, which will be persisted and can be used for correlation.
**The typical workflow:**
1. **Configure authentication to capture record identifiers**: Set up your authentication provider
or an [interaction hook](/docs/issuance/authorization-code/interaction-hook/overview) to capture
a database record identifier during the authentication flow. This could be passed as a request
parameter or retrieved from your systems based on the authenticated user.
2. **Persist the record identifier**: Use `claimsToPersist` to store the database record identifier
in the MATTR VII user record, or pass it through the issuance flow.
3. **Handle the credential issuance webhook**: When a credential is claimed, your webhook handler
receives an event. Retrieve the database record identifier from the user's claims or from custom
claims in the webhook payload, then store the `credentialId` against that database record.
4. **Revoke when the record changes**: When changes to your database require revoking the credential
(such as a license expiration or permit suspension), retrieve the stored `credentialId` from your
database record and use it to revoke the credential.
This approach typically requires additional integration work such as configuring interaction hooks
or passing request parameters through your authentication flow. For most use cases where you want
to associate credentials with database records, the Pre-Authorized Code flow provides a simpler and more direct solution.
##### Correlate to users in your system [#correlate-to-users-in-your-system]
Use this approach when you need to associate credentials with specific users. Each authenticated user
receives a credential that should be tied to their user record in your system. You need to revoke
credentials when a user's status changes (such as when a membership expires or an employee leaves).
**The typical workflow:**
1. **Configure your authentication provider to persist identifiers**: Set up your authentication
provider to persist non-PII identifiers (such as the `sub` claim or an external user ID) to the
MATTR VII user record. These identifiers will be used to correlate authenticated users with user
records in your database.
2. **Create a credential offer for authenticated users**: Create an Authorization Code credential
offer that any eligible user can claim after authenticating. You don't know in advance who will
claim it or when.
3. **Handle the credential issuance webhook**: When a credential is claimed, your webhook handler
receives an event that includes the `userId` and `credentialId`. Retrieve the MATTR VII user
record using the `userId` to access the persisted identifier (such as
`authenticationProvider.subjectId`). Use this identifier to locate the corresponding user in your
database, then store the `credentialId` against that user.
4. **Revoke when the user's status changes**: When a user's status changes and requires credential
revocation (such as membership expiration or employment termination), retrieve all stored
`credentialId` values for that user and revoke them.
**Configure your authentication provider to persist identifiers**
When using an authentication provider, configure it to persist identifying claims from the identity
provider to the MATTR VII user record. This enables you to later correlate MATTR VII users with
user records in your external systems.
Avoid persisting Personally Identifiable Information (PII) such as email addresses or phone
numbers in MATTR VII user claims. Instead, persist external identifiers (such as user IDs or
account numbers from your system) or the authentication provider's `sub` claim. These
non-sensitive identifiers can be correlated internally with your systems without exposing PII.
Set the `claimsToPersist` field in your
[Authentication provider configuration](/docs/api-reference/platform/authentication-provider/createAuthenticationProvider)
to include relevant user identifiers:
```json title="Authentication provider configuration (partial)"
{
"claimsToPersist": ["sub"]
}
```
When a user authenticates and claims a credential, MATTR VII persists the specified claims from the
identity provider into the user's `claims` object. The `authenticationProvider.subjectId` is also
automatically stored with the user record.
**Handle the webhook and store the credential ID**
When your webhook handler receives the `OpenIdCredentialIssuedSummary` event, use the `userId` to
look up the MATTR VII user record and retrieve the persisted identifier:
```http title="Request"
GET /v1/users/{userId}
```
```json title="Response"
{
"id": "7382276d-ef75-4d17-8fb0-1d3aec4647ab",
"claims": {
"sub": "auth0|abc123"
},
"authenticationProvider": {
"subjectId": "auth0|abc123"
}
}
```
Use the `sub` or `authenticationProvider.subjectId` value to locate the corresponding user in your
database, then store the `credentialId` from the webhook payload against that user so you can revoke
their credentials if their status changes.
```json title="Webhook payload"
{
"credentialId": "9613ac5e-a0ba-4512-ba0b-90e91b2744bc", // [!code focus]
"userId": "7382276d-ef75-4d17-8fb0-1d3aec4647ab",
"credentialConfigurationId": "1d8c7ad7-84ce-4519-8365-7af986e4ee0e"
}
```
### Correlation strategy summary [#correlation-strategy-summary]
| Issuance flow | Correlation method | What it associates | When to use |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| Pre-Authorized Code | Match `credentialOfferId` from webhook to stored offer ID | Database record (permit, license, enrollment) | Each credential represents a specific record that can change independently of the user |
| Pre-Authorized Code | Match `userClaims.externalUserId` from webhook to user in your system | Specific user | Each credential is tied to a user; revoke when user status changes (termination, expiration) |
| Authorization Code | Look up `userId`, retrieve `authenticationProvider.subjectId`, match to user in your system | Specific user | User authenticates to claim; credentials tied to authenticated user identity |
| Authorization Code | Use custom claims or interaction hooks to capture database record identifier | Database record (via custom integration) | Advanced: Credential represents a record, requires additional integration with authentication flow |
| Both | Query [user credentials endpoint](/docs/api-reference/platform/users/getUserCredentials) to list all credentials for a user | Specific user | You want to find all credentials associated with a specific user |
## Step 3: Revoke a credential [#step-3-revoke-a-credential]
Once you have the `credentialId` of the credential you want to revoke, use MATTR VII to update its
status.
Currently this is only available via the API:
Make a request to
[update the mDoc status](/docs/api-reference/platform/mdocs-status/postCredentialStatus):
```http title="Request"
POST /v2/credentials/mobile/{credentialId}/status
```
```json title="Request body"
{
"status": "invalid"
}
```
* `credentialId`: Replace with the credential identifier you captured from the webhook or the user
credentials endpoint.
```json title="Response"
{
"status": "invalid"
}
```
Setting the status to `invalid` is **irreversible**. Once a credential is invalidated, it cannot
be restored to a valid state. Use this only when you intend to permanently revoke the credential.
## What holders and verifiers see [#what-holders-and-verifiers-see]
### Holders [#holders]
Once a credential is revoked, the holder's wallet should reflect the status change. The credential remains
in the wallet but is marked as revoked or invalid, depending on the wallet implementation.
Here is an example of how a revoked credential might appear in MATTR GO Hold:
### Verifiers [#verifiers]
When a verifier attempts to verify a revoked credential, the verification process checks the
credential's status against the status list. If the credential has been revoked:
* Verification **fails**.
* The verifier is informed that the credential has been **revoked**.
* Credential details (such as name, portrait, or address) can still be shown, depending on the verifier's implementation. MATTR Verifier SDKs will show the details but indicate the revoked status clearly. However, third-party verifiers that you do not control may choose to hide all credential details when a credential is revoked.
Here is an example of how a revoked credential might appear in MATTR GO Verify:
MATTR platforms check revocation status in a privacy-preserving manner by retrieving publicly
available status lists that hold revocation information for multiple credentials. This means the
issuer cannot determine which specific credentials are being checked by the verifier or for what
purpose.
As mDocs revocation is an emerging standard, relying parties that have not implemented revocation
checks might still assess a presented mDoc as valid, even when it has been revoked by the issuer.
## Revocation propagation timing [#revocation-propagation-timing]
When you revoke a credential, the status change is not immediately visible to all holders and
verifiers. The time it takes for revocation to propagate depends on the **Status list configuration**
for the credential's `docType`, specifically the `timeToLiveDuration` (TTL) and `expiryDuration`
(EXP) values.
* **TTL (Time To Live)**: The recommended duration a relying party should cache a status list token
before retrieving a new one. This is a guideline; relying parties should respect it but are not
strictly enforced to do so.
* **EXP (Expiry)**: The maximum duration a status list token remains valid. After this time, a
relying party **must** retrieve a new token. They cannot use an expired token to check credential
status.
The default values are:
| Setting | Default | Minimum | Maximum |
| ------- | ------- | ------- | ------------------------------- |
| TTL | 1 day | N/A | Cannot exceed EXP |
| EXP | 1 week | N/A | Cannot exceed the IACA validity |
### How status list caching works [#how-status-list-caching-works]
Status list tokens are cached to improve performance and enable offline verification. The following
diagram illustrates how TTL and EXP affect the revocation propagation timeline:
Status list tokens are cached to improve performance and enable offline verification:
1. **You revoke a credential**: The status list is updated immediately on your MATTR VII tenant. If
the current status list token is cached, the cache is cleared and a new token is signed.
2. **Holder or verifier requests the status list**: If they have a locally cached copy of the status
list token, they use it until the TTL expires. Once the TTL expires, they retrieve a fresh token.
3. **Fresh token reflects the revocation**: The newly retrieved status list token includes the
updated status for the revoked credential.
**Key points from the diagram:**
* The TTL (Time To Live) determines when compliant wallets and verifiers should retrieve a fresh
status list token. In this example, with a 1-day TTL, the revocation becomes visible when the
wallet fetches a new token on Day 4 (24 hours after the previous fetch).
* If a wallet or verifier does not respect the TTL recommendation, they may continue using the cached
token until it reaches its EXP (Expiry). In this example, with a 1-week EXP, the revocation would
not be visible until Day 7 at the latest.
* MATTR SDKs respect TTL values, but you cannot guarantee the behavior of third-party wallets and
verifiers that you do not control.
### Balancing freshness and offline capability [#balancing-freshness-and-offline-capability]
Status list caching represents a deliberate trade-off between two important requirements:
**Ensuring up-to-date credential status:**
* Shorter TTL and EXP values mean wallets and verifiers fetch fresh status list tokens more
frequently.
* This reduces the time between when you revoke a credential and when holders and verifiers see the
updated status.
* However, it requires more network requests and means wallets and verifiers must be online more
frequently to verify credentials.
**Supporting offline verification:**
* Longer TTL and EXP values enable wallets and verifiers to cache status list tokens for extended
periods.
* This allows credential verification to continue in offline or low-connectivity environments without
requiring constant network access.
* However, it increases the time between when you revoke a credential and when the revocation becomes
visible to holders and verifiers who already have a cached token.
**Choosing the right balance:**
The optimal TTL and EXP values depend on your use case:
* **High-security credentials** (e.g., employee access badges, time-sensitive permits): Use shorter
TTL/EXP values to ensure revocations propagate quickly. Accept that verification may fail in
offline scenarios.
* **Offline-first credentials** (e.g., travel documents, educational certificates): Use longer
TTL/EXP values to maximize offline verification capability. Accept that revocations may take longer
to propagate.
* **Balanced approach** (default): TTL of 1 day and EXP of 1 week provides reasonable freshness while
still supporting some offline verification scenarios.
If your verification workflows predominantly occur in controlled environments with reliable network
connectivity (such as border control or workplace access), you can prioritize freshness with
shorter TTL/EXP values. If your credentials are frequently verified in remote or low-connectivity
environments (such as rural areas or international travel), prioritize offline capability with
longer values.
### Configuring TTL and EXP [#configuring-ttl-and-exp]
You can adjust these values using a
[Status list configuration](/docs/api-reference/platform/status-list-configuration/createStatusListConfiguration).
Status list configurations are unique per `docType` per tenant.
When you update the status list configuration (e.g. change TTL from 24 hours to 2 hours), holders and verifiers who already have a cached token will continue using the old values until their cached token's TTL expires or the token reaches its EXP.
Currently, status list configurations can only be managed via the API:
```http title="Request"
POST /v2/credentials/mobile/status-lists/configurations
```
```json title="Request body"
{
"docType": "org.iso.18013.5.1.mDL",
"timeToLiveDuration": {
"hours": 2
},
"expiryDuration": {
"hours": 12
}
}
```
To update an existing configuration:
```http title="Request"
PUT /v2/credentials/mobile/status-lists/configurations/{id}
```
```json title="Request body"
{
"timeToLiveDuration": {
"hours": 2
},
"expiryDuration": {
"hours": 12
}
}
```
### Propagation scenarios [#propagation-scenarios]
The following scenarios illustrate how different TTL and EXP configurations affect when revocation
becomes visible.
#### Scenario 1: Fast propagation [#scenario-1-fast-propagation]
* **TTL**: 1 hour
* **EXP**: 6 hours
In this scenario, relying parties retrieve a fresh status list token at least every hour. If you
revoke a credential, a compliant relying party will see the revocation within 1 hour or less. This
is suitable when timely revocation is critical, but results in more frequent requests to your
tenant's status list endpoint.
#### Scenario 2: Standard propagation (default) [#scenario-2-standard-propagation-default]
* **TTL**: 1 day
* **EXP**: 1 week
Relying parties cache the status list token for up to 1 day before retrieving a new one. If you
revoke a credential, it may take up to 24 hours for the revocation to propagate across all relying
parties. Even if a relying party ignores the TTL, the token expires after 1 week, forcing a refresh.
#### Scenario 3: Offline-optimized [#scenario-3-offline-optimized]
* **TTL**: 3 days
* **EXP**: 2 weeks
This configuration is suited for scenarios where verifiers operate frequently in offline
environments and need to cache status list tokens for extended periods. The trade-off is that
revocation may take up to 3 days to propagate under normal conditions.
## Related resources [#related-resources]
* [Revocation overview](/docs/issuance/revocation/overview): Conceptual overview of how revocation
works for mDocs and CWT credentials.
* [Revocation tutorial](/docs/issuance/revocation/tutorial): Step-by-step walkthrough of issuing,
checking, and revoking a credential.
* [Users guide](/docs/issuance/users/guide): How to manage user associations throughout the
credential issuance lifecycle.
* [Webhooks guide](/docs/platform-management/webhooks-guide): How to configure and handle webhook
events.
* [mDocs status API reference](/docs/issuance/revocation/api-reference/mdocs-status-update): Update
and retrieve mDocs revocation status.
* [Status list configuration API reference](/docs/issuance/revocation/api-reference/mdocs-status-list-configuration):
Manage status list TTL and EXP settings.
# Credential revocation journey pattern
URL: /docs/issuance/revocation/journey-pattern
This journey pattern enables issuers to update the status of issued credentials. Verifiers can check the status of a presented credential without compromising the holder’s privacy.
## Overview [#overview]
* **Formats**: mDocs, CWT, Semantic CWT
## Journey flow [#journey-flow]
## Architecture [#architecture]
### Credential issuance [#credential-issuance]
The issuer uses MATTR VII capabilities to issue a credential in a way that enables it to be revoked
at a later date. Revocable credentials include a reference to a revocation list that reflects the most
up-to-date status of credentials included in it.
This does not affect the issuance workflow or how would the credential look like to the holder.
### Credential revocation [#credential-revocation]
When required, the issuer can use MATTR VII capabilities to revoke the issued credential. This will
update the status of the credential in the referenced status list.
### Revocation notification [#revocation-notification]
The issuer can use MATTR VII capabilities or their existing communication channels to notify the
holder that the credential has been revoked.
### Verifying a revoked credential [#verifying-a-revoked-credential]
When a relying party attempts to verify a revoked credential, they can use MATTR VII or MATTR Pi
capabilities that check the revocation status of the credential as part of the verification process.
If the credential is found to be revoked, verification will fail and the verifier will be notified
of the relevant failure reason.
MATTR platforms check the revocation status in a privacy preserving manner by checking the publicly
accessible revocation lists which hold the revocation information for multiple credentials. This way the
issuer cannot tell what credentials were actually checked by the relying party and for what purpose.
# Revocation
URL: /docs/issuance/revocation/overview
## Overview [#overview]
Issuers might need to invalidate a previously issued digital credential before its expiry date. This
might be required when a credential has been compromised, its status and/or claims have changed, or
in cases when a credential is issued in error.
You can use MATTR VII to change the status of issued credentials if you ever need to revoke or suspend
them for any reason. Verifiers can then obtain the status of presented credentials in a way that preserves
holders’ privacy.
The implementation differs based on your chosen credential format:
* [mDocs](#mdocs)
* [CWT credentials](#cwt)
## mDocs [#mdocs]
MATTR's implementation of mDocs revocation is based on Draft 14 of the IETF
[Token Status List](https://drafts.oauth.net/draft-ietf-oauth-status-list/draft-ietf-oauth-status-list.html) specification. A
revocable mDoc includes a reference to a status list which is managed by the issuer. The list
contains the revocation status of multiple credentials, and each credential references the index of
its status within a specific status list.
Status lists are automatically created and managed when you use your MATTR VII tenant to issue
revocable mDocs. They are publicly available and mainly consumed by external verifiers to check the
status of presented mDocs.
When a relying party retrieves a status list, the issuer cannot tell what specific mDoc they are
checking the status for. This means the issuer does not know how often or to whom an mDoc is being
presented, maintaining holder's privacy.
As mDocs revocation is an emerging standard, relying parties are not enforced
to implement it. As a result, relying parties which have not implemented
revocation checks might assess a presented mDoc as valid, even when it was
revoked by the issuer.
### Status values [#status-values]
The following status values are available for revocable mDocs:
* **Valid**: The mDoc is valid. Valid mDocs can be updated to `Invalid`.
* **Invalid**: The mDoc is revoked. This state is irreversible - once an mDoc status was updated to
`Invalid`, it cannot be updated to any other status. This value should be used for permanently
revoking an mDoc.
* **Suspended**: ⚠️ **Deprecated** ⚠️ The mDoc is temporarily revoked. Suspended mDocs can be updated to `Valid` or
`Invalid`. This value should be used for temporarily revoking an mDoc.
### Revocable mDocs [#revocable-mdocs]
By default signed mDocs are not revocable. To issue revocable mDocs and enable changing their status
at a later date, the request payload must set `includeStatus` to `true`. This can be configured when
creating an [mDoc credential configuration](/docs/issuance/credential-configuration/overview).
This results in the following:
* The mDoc status is added to a status list.
* The issued mDoc includes a `status` object in its [MSO payload](/docs/concepts/mdocs/structure-to-function)
that references this status list:
```
"status": {
"status_list": {
"uri": "https://learn.vii.au01.mattr.global/v2/credentials/mobile/status-lists/f331c9be-f526-4577-bbac-ae93d6228f7a/token",
"idx": 0
}
}
```
* `status` : This field includes the information regarding the revocation status of this mDoc. It is added to the MSO payload of the mDoc when `includeStatus` is set to `true` in the credential configuration. Legacy credentials issued before the adoption of Draft 14 of the Token Status
List specification use the `_status` field name which is now deprecated. The `status` field includes the following properties:
* `status_list` : References the status list that holds the status information for this mDoc.
* `uri` : Relying parties can use this publicly available endpoint to retrieve the status list token.
* `idx` : Indicates the index of the mDoc status within the referenced status list.
### Status list [#status-list]
MATTR VII automatically creates and manages status lists when revocable mDocs are signed.
A status list is a bit string where each credential status is mapped to a position in the list. The number of bits per credential depends on the tenant configuration:
**1-bit status lists** (latest specification as per Draft 14 of the Token Status List specification):
* Each credential occupies **one bit**:
* `0`: Valid
* `1`: Invalid
* Each list can hold the status of up to **500,000** individual mDocs.
**2-bit status lists** (legacy format, deprecated with the adoption of Draft 14 of the Token Status List specification):
* Each credential occupies **two bits**:
* `00`: Valid
* `01`: Invalid
* `10`: Suspended
* Each list can hold the status of up to **500,000** individual mDocs.
The bit configuration is specified in the status list token's `bits` field and is determined by your tenant's configuration.
### Status list tokens [#status-list-tokens]
When a relying party attempts to retrieve a status list the response does not include the raw status
list, but rather a signed version which is referred to as a status list token. This is a
[CBOR Web Token](https://datatracker.ietf.org/doc/html/rfc8392).
#### Token header [#token-header]
The token header includes a `typ` (type) field that indicates the token's format:
* **Latest specification**: `application/statuslist+cwt`
* **Legacy format**: `mattr-statuslist+cwt`
Relying parties can use this field to automatically detect and handle both formats.
#### Token payload [#token-payload]
The token payload includes the following information:
* `iat` (CBOR claim 6): Timestamp when the status list token was signed.
* `sub` (CBOR claim 2): The URI that was used to retrieve the token. This must match the `uri` from the mDoc `status` object.
* `exp` (CBOR claim 4): Timestamp at which the token expires and the relying party can no longer use it to check the mDoc status. This prevents relying parties from using locally cached tokens in their verification workflows for too long, and forces them to retrieve a new token with the most up-to-date mDocs status. Defaults to one week from when the token was signed but can be adjusted using a [Status list configuration](#status-list-configuration). Regardless of the Status list configuration, `exp` can never exceed the validity of the IACA used to sign the Status list token.
* `ttl` (CBOR claim 65534 in latest specification, -65539 in legacy): Time To Live - Recommended duration (in seconds) the relying party should use this token before retrieving a new one. This guides best practices for relying parties to ensure they are using the most up-to-date mDocs status in their verification workflows. Defaults to one day but can be adjusted using a [Status list configuration](#status-list-configuration). Regardless of the Status list configuration, `ttl` can never exceed `exp`.
* `status_list` (CBOR claim 65533 in latest specification, -65538 in legacy):
* `bits`: Details the number of bits each mDoc occupies in the list (1 for latest specification, 2 for legacy).
* `lst`: Compressed zlib byte string containing the status of all mDocs included in this status list.
When CBOR claim numbers differ between formats, relying parties must detect
the format based on the token header `typ` field and parse the payload
accordingly. This is automatically handled by MATTR Holder and Verifier SDKs.
#### Token signature [#token-signature]
Status list tokens are signed by Status list signers. By default, MATTR VII automatically creates
and manages these signers. If you use
[unmanaged certificates](/docs/concepts/chain-of-trust#external-certificates), you are responsible for managing
your own Status list signers and their certificates.
#### Caching [#caching]
Status list tokens are automatically managed by MATTR VII and cached for improved performance. Once
a new Status list token is signed, it can be cached for a minimum time of 1 minute and up to a
maximum of 24 hours. The cache is cleared and a new Status list token is signed in the following
scenarios:
* The Status list token has reached its maximal caching limit (24 hours).
* The referenced [Status list configuration](#status-list-configuration) is updated.
* The status list is updated (e.g. the status of an mDoc that is included in the status list is
changed).
#### When TTL configuration changes take effect [#when-ttl-configuration-changes-take-effect]
If you update the status list configuration to change the TTL or EXP values, these changes apply to the entire status list the next time it is signed.
**The status list token is re-signed in the following scenarios:**
* The current TTL expires
* A credential on the list is updated (e.g., revoked or suspended)
* A new credential is added to the list
* The status list configuration is updated
**On the holder or verifier side**, the updated status list token (with the new TTL/expiry values) is retrieved when:
* The current cached status list token's TTL expires
* The holder/verifier requests the status list after you have signed the new version
**Example scenario**: If you issue a credential with a 24-hour TTL, then
immediately update the status list configuration to use a 2-hour TTL, holders
and verifiers will continue using the 24-hour TTL until the status list token
is re-signed. This happens when the original 24-hour TTL expires, or when any
credential on that list is updated, or when a new credential is added. Once
the new token is signed, subsequent retrievals will use the new 2-hour TTL.
#### IACA deletion [#iaca-deletion]
Status list tokens must be signed using the same
[IACA](/docs/issuance/certificates/overview#issuing-authority-certificate-authority-iaca) that was used to sign the
mDocs that are included in the status list. If this IACA is deleted, all status lists signed by it
are deleted as well, and relying parties will not be able to retrieve new Status list tokens that
include the status of these mDocs. When attempting to manually retrieve the revocation status of
these mDocs, the returned status will be `Unknown`.
When an IACA is deleted, relying parties can still use locally cached copies
of Status list token signed by this IACA until they reach their set `exp`.
### Status list configuration [#status-list-configuration]
Issuers can optionally create status list configurations to manually adjust the Status list `ttl`
and `exp` values for a specific `docType`. This enables issuers to adjust the duration for which
relying parties can use a status list token before being forced to retrieve a new copy.
Status list configurations are unique per `docType` per tenant and are referenced by the Status
lists that include mDocs of this `docType`.
If no Status list configuration is available, MATTR VII will automatically create one when a new
Status list is created based on the default `ttl` (one day) and `exp` (one week) values.
### Status list distribution [#status-list-distribution]
Any relying party is able to retrieve an Issuer's status lists and cache them locally to support
offline verification workflows.
This is achieved by making a GET request to the
[distribution endpoint](/docs/issuance/revocation/api-reference/mdocs-status-list-retrieval#status-list-distribution).
This endpoint is public and does not require any authentication. It is referenced in the issuer's
IACA `X509v3 extensions` element under the `1.3.6.1.4.1.61546.100` OID, as shown in the following
example:
```
X509v3 extensions:
1.3.6.1.4.1.61546.100:
https://learn.vii.au01.mattr.global/v2/credentials/mobile/status-lists/distribution
```
The response will include a `status_lists` array which details the URIs of the different Status list
tokens that are available on this MATTR VII tenant. The response format varies based on your tenant configuration. Relying parties must support both response formats for compatibility. MATTR SDKs automatically handle both formats:
**Draft 14 Specification format:**
```json title="Status lists response (Draft 14 Specification)"
{
"status_lists": [
"https://learn.vii.au01.mattr.global/v2/credentials/mobile/status-lists/{id}/token",
"https://learn.vii.au01.mattr.global/v2/credentials/mobile/status-lists/{id2}/token"
]
}
```
**Legacy format:**
```json title="Status lists response (Legacy format)"
{
"status_lists": [
{
"uri": "https://learn.vii.au01.mattr.global/v2/credentials/mobile/status-lists/{id}/token"
},
{
"uri": "https://learn.vii.au01.mattr.global/v2/credentials/mobile/status-lists/{id2}/token"
}
]
}
```
Status list tokens signed by expired IACAs are excluded from the response.
### Status list signers [#status-list-signers]
Status list tokens are signed by Status list signers. By default, MATTR VII automatically creates
and manages these signers and their Status List Signer Certificates (SLSCs). If you use
[unmanaged certificates](/docs/concepts/chain-of-trust#external-certificates), you are responsible for managing
your own Status list signers and their certificates.
Managed SLSCs may be [revoked](/docs/issuance/revocation/api-reference/mdocs-status-list-signers#revoke-a-status-list-signer) for reasons such as key compromise, role change, or decommissioning. Once revoked, the SLSC’s serial number is published in the Certificate Revocation List (CRL) referenced by its issuing IACA.
Verifiers that retrieve and process the CRL referenced in the IACA must treat a revoked SLSC, and any Status lists it signed, as invalid.
### Backward compatibility [#backward-compatibility]
MATTR VII maintains backward compatibility between the legacy and Draft 14 Token Status List formats to ensure existing implementations continue to function while enabling adoption of the standardized specification.
#### Format selection [#format-selection]
* Tenants can opt into the latest Token Status List specification via a feature flag. [Contact us](mailto:dev-support@mattr.global) to enable this for your tenant.
* Legacy format continues to be supported for existing implementations.
* The format is determined at credential issuance time based on tenant configuration.
* Individual credentials cannot change formats after issuance.
#### Key differences between formats [#key-differences-between-formats]
The following table summarizes the differences between the legacy MATTR format and the latest Token Status List specification:
| Aspect | Legacy Format | Latest Specification |
| -------------------------------- | -------------------------------------- | ---------------------------- |
| **MSO field name** | `_status` | `status` |
| **Token header `typ`** | `mattr-statuslist+cwt` | `application/statuslist+cwt` |
| **Status bits per credential** | 2 bits | 1 bit |
| **Available status values** | Valid, Invalid, Suspended (Deprecated) | Valid, Invalid |
| **CBOR payload claim numbers** | Negative keys (-65538, -65539) | Positive keys (65533, 65534) |
| **Distribution response format** | Array of objects with `uri` property | Array of URI strings |
| **Maximum credentials per list** | 500,000 | 500,000 |
#### SDK and verifier compatibility [#sdk-and-verifier-compatibility]
* **MATTR SDKs** automatically detect and handle both formats based on the token header `typ` field
* **Verifiers** must support both distribution endpoint response structures
* **Status list tokens** are self-describing - the `typ` header identifies the format
#### Migration considerations [#migration-considerations]
When a tenant opts into the latest specification:
* All **new** credentials are issued using the new format
* **Existing** credentials continue to use their original format
* Both formats can coexist within the same tenant
* Status lists are maintained separately per format to prevent conflicts
## CWT [#cwt]
You can use MATTR VII to change the status of issued CWT credentials if you ever need to revoke them
for any reason. Verifiers can obtain the revocation status of presented credentials in a way that
preserves holders’ privacy.
A revocable CWT credential points to a revocation list which is managed by the credential\`s issuer.
The list contains the revocation status of many credentials, and the credential references the index
of its status within a specific revocation list.
Revocation lists are automatically created and managed when you use your MATTR VII tenant to issue
revocable credentials. They are publicly available and mainly consumed by external verifiers.
When a verifier requests a revocation list, the issuer cannot tell what credential they are checking
the status for. This means the issuer does not know how often or to whom a credential is being
presented, maintaining holder\`s privacy.
### `credentialStatus` object [#credentialstatus-object]
When a CWT credential is set as revocable the `credentialStatus` object is added to the credential:
```json title="credentialStatus object"
"status": {
"index": 4,
"url": "https://learn.vii.au01.mattr.global/v2/credentials/compact/revocation-lists/f91bbef3-6898-4930-bca3-cf0d4b63e939"
}
```
* `index` : This indicates the index of this specific credential within the revocation list.
* `url` : Every revocable credential will reference a Revocation List that is automatically
created and held on the issuer's tenant. This list can be used by external verifiers to validate
the credential status. The `status.url` property references the revocation list which holds the
revocation status for this specific credential.
# Learn how to manage the revocation status of issued credentials
URL: /docs/issuance/revocation/tutorial
## Overview [#overview]
In this tutorial we will explore the concept of
[credential revocation](/docs/issuance/revocation/overview), a critical feature that allows
issuers to invalidate previously issued credentials, ensuring the integrity and security of the
system. The tutorial comprises the following steps:
1. Issue a revocable credential.
2. Obtain a credential revocation status.
3. Revoke an issued credential.
4. Attempt to verify a revoked credential.
## Prerequisites [#prerequisites]
* Complete the [sign up form](/docs/resources/get-started) to get trial access to MATTR VII and
the MATTR Portal, and then [Create a tenant](/docs/platform-management/portal#creating-a-tenant).
* Install the MATTR GO Hold example app by following the [getting started guide](/docs/holding/go-hold/getting-started).
We recommend using the MATTR VII [Postman
collection](/docs/api-reference#postman-collection)
in this tutorial. While this isn't an explicit prerequisite, it can really
speed things up.
## Tutorial steps [#tutorial-steps]
### Issue a revocable credential [#issue-a-revocable-credential]
The first thing you need to do is sign a new credential in a way that will enable you to revoke it
later. This will differ slightly based on your selected credential format.
1. Follow the [Authorization Code](/docs/issuance/authorization-code/tutorial) or [Pre-authorized Code](/docs/issuance/pre-authorized-code/tutorial) tutorials to issue a credential.
2. By the end of the tutorial you should have an mDoc in your MATTR GO Hold example app.
1. Make a request of the following structure to
[create a `did:web`](/docs/api-reference/platform/dids/createDid):
```http title="Request"
POST /v1/dids
```
```json title="Request body"
{
"method": "web",
"options": {
"url": "https://learn.vii.au01.mattr.global" // [!code highlight]
}
}
```
* `url` : Replace with your `tenant_url` provided with your
[tenant details](/docs/platform-management/portal#getting-started).
*Response*
```json title="Response body"
{
"did": "did:web:learn.vii.au01.mattr.global" // [!code focus]
// Rest of DID document
}
```
* `did` : We will use the value of this element to identify the credential's issuer in the next
step.
If your tenant has a [Custom domain](/docs/platform-management/custom-domain-overview)
configured you will need to [setup a
redirect](/docs/platform-management/custom-domain-guide#create-redirects-for-required-assets)
for this DID document.
2. Make a request of the following structure to
[create and sign a new revocable CWT credential](/docs/issuance/direct-issuance-api-reference/cwt-issuance#sign-a-cwt-credential):
```http title="Request"
POST /v2/credentials/compact/sign
```
```json title="Request body"
{
"payload": {
"iss": "did:web:learn.vii.au01.mattr.global", // [!code highlight]
"type": "Course Credential",
"name": "Emma Jane Tasma",
"code": "HS.278",
"certificationName": "Working at Heights",
"certificationLevel": "Level 4",
"issuerName": "Advanced Safety Training"
},
"revocable": true, // [!code highlight]
"isRevoked": false // [!code highlight]
}
```
* `iss` : Replace with the `did` element obtained in the previous step.
* `revocable`: We set this to `true` so that this credential can be revoked later in the tutorial.
* `isRevoked`: We set this to `false` so that this credential is immediately valid upon issuance.
Other use cases might require you to set this to `true` to issue a credential that is revoked by
default until it is activated by unrevoking.
*Response*
```json title="Response body"
{
"id": "bKcrxojFSuSZvI5qhKInxA", // [!code highlight]
"decoded": {
"iss": "did:web:learn.vii.au01.mattr.global",
"type": "Course Credential",
"name": "Emma Jane Tasma",
"code": "HS.278",
"certificationName": "Working at Heights",
"certificationLevel": "Level 4",
"issuerName": "Advanced Safety Training",
"status": { // [!code highlight]
"index": 3, // [!code highlight]
"url": "https://learn.vii.au01.mattr.global/v2/credentials/compact/revocation-lists/887cd140-e4d7-4518-b70f-305b23778848" // [!code highlight]
},
"jti": "bKcrxojFSuSZvI5qhKInxA"
},
"encoded": "CSC:/1/2KCE3IQEJB5DCMSMGZITM5QBE2QFSALWVQAXQI3ENFSDU53FMI5GYZLBOJXC45TJNEXGC5JQGEXG2YLUORZC4Z3MN5RGC3AFDJSZE7YQHIAACAACOFBW65LSONSSAQ3SMVSGK3TUNFQWYBA2NFLDPEDENZQW2ZLPIVWW2YJAJJQW4ZJAKRQXG3LBMRRW6ZDFMZEFGLRSG44HCY3FOJ2GSZTJMNQXI2LPNZHGC3LFOJLW64TLNFXGOIDBOQQEQZLJM5UHI43SMNSXE5DJMZUWGYLUNFXW4TDFOZSWYZ2MMV3GK3BAGRVGS43TOVSXETTBNVSXQGCBMR3GC3TDMVSCAU3BMZSXI6JAKRZGC2LONFXGOZTFPBYGS4TZNIZDAMRWFUYDCLJQGE5AAAIAACRAEAYDPB2WQ5DUOBZTULZPNRSWC4TOFZ3GS2JOMF2TAMJONVQXI5DSFZTWY33CMFWC6Y3POJSS65RSF5RXEZLEMVXHI2LBNRZS6Y3PNVYGCY3UF5ZGK5TPMNQXI2LPNYWWY2LTORZS6OBYG5RWIMJUGAWWKNDEG4WTINJRHAWWENZQMYWTGMBVMIZDGNZXHA4DIOAH3BAFA3FHFPDIRRKK4SM3ZDTKQSRCPRCYIA7RFUZYQI3RIGDHIGLAODJ6K2F245DTLIIKXAD35TORFQ7MVRJCIEPH6SC6NGA4HRMK76H5V6GXP66FFNX7MNYC6MYVU7ZLLXYVLXBU"
}
```
* `id` : Unique credential identifier. We will use it in the next step to obtain the credential's
revocation status, and later to revoke the credential.
* `status`: Credential revocation status details. We will use it in the next step to obtain the
credential's revocation status.
1. Make a request of the following structure to
[create a `did:web`](/docs/api-reference/platform/dids/createDid):
```http title="Request"
POST /v1/dids
```
```json title="Request body"
{
"method": "web",
"options": {
"url": "https://learn.vii.au01.mattr.global" // [!code highlight]
}
}
```
* `url` : Replace with your `tenant_url` provided with your
[tenant details](/docs/platform-management/portal#getting-started).
*Response*
```json title="Response body"
{
"did": "did:web:learn.vii.au01.mattr.global" // [!code focus]
// Rest of DID document
}
```
* `did` : We will use the value of this element to identify the credential's issuer in the next
step.
If your tenant has a [Custom domain](/docs/platform-management/custom-domain-overview)
configured you will need to [setup a
redirect](/docs/platform-management/custom-domain-guide#create-redirects-for-required-assets)
for this DID document.
2. Make a request of the following structure to
[sign a new revocable Semantic CWT credential](/docs/issuance/direct-issuance-api-reference/semantic-cwt-issuance#sign-a-semantic-cwt-credential):
```http title="Request"
POST /v2/credentials/compact-semantic/sign
```
```json title="Request body"
{
"payload": {
"iss": "did:web:learn.vii.au01.mattr.global", // [!code highlight]
"vc": {
"type": "Course Credential",
"credentialSubject": {
"name": "Emma Tasma",
"code": "HS.278",
"certificationName": "Working at Heights",
"certificationLevel": "Level 4",
"issuerName": "Advanced Safety Training"
}
}
},
"revocable": true, // [!code highlight]
"isRevoked": false // [!code highlight]
}
```
* `iss` : Replace with the `did` element obtained in the previous step.
* `revocable`: We set this to `true` so that this credential can be revoked later in the tutorial.
* `isRevoked`: We set this to `false` so that this credential is immediately valid upon issuance.
Other use cases might require you to set this to `true` to issue a credential that is revoked by
default until it is activated by unrevoking.
*Response*
```json title="Response body"
{
"id": "urn:uuid:78e19496-8521-424b-8315-35fb1ecaf681", // [!code focus]
"decoded": {
"iss": "did:web:learn.vii.au01.mattr.global",
"vc": {
"type": ["VerifiableCredential", "Course Credential"],
"@context": ["https://www.w3.org/2018/credentials/v1"],
"credentialSubject": {
"name": "Emma Tasma",
"code": "HS.278",
"certificationName": "Working at Heights",
"certificationLevel": "Level 4",
"issuerName": "Advanced Safety Training"
}
},
"status": { // [!code focus]
"index": 0, // [!code focus]
"url": "https://learn.vii.au01.mattr.global/v2/credentials/compact-semantic/revocation-lists/1fe00d6c-904f-497e-bbe1-a3cfdc0b8368" // [!code focus]
},
"jti": "urn:uuid:78e19496-8521-424b-8315-35fb1ecaf681"
},
"encoded": "CSS:/1/2KCE3IQEJB5DCMSMGZITM5QBE2QFSAOZUYAXQI3ENFSDU53FMI5GYZLBOJXC45TJNEXGC5JQGEXG2YLUORZC4Z3MN5RGC3AFDJSZE7YQMJ3GHI3IIBRW63TUMV4HJALYEZUHI5DQOM5C6L3XO53S45ZTFZXXEZZPGIYDCOBPMNZGKZDFNZ2GSYLMOMXXMMLEOR4XAZMCORLGK4TJMZUWCYTMMVBXEZLEMVXHI2LBNRYUG33VOJZWKICDOJSWIZLOORUWC3DRMNZGKZDFNZ2GSYLMKN2WE2TFMN2KMZDOMFWWK2SFNVWWCICUMFZW2YLEMNXWIZLGJBJS4MRXHBYWGZLSORUWM2LDMF2GS33OJZQW2ZLSK5XXE23JNZTSAYLUEBEGK2LHNB2HG4TDMVZHI2LGNFRWC5DJN5XEYZLWMVWGOTDFOZSWYIBUNJUXG43VMVZE4YLNMV4BQQLEOZQW4Y3FMQQFGYLGMV2HSICUOJQWS3TJNZTWMZLYOBUXE6LKGIYDENRNGAYS2MBRAQNGSVRXSA5AAAIAACRAEAADPB7GQ5DUOBZTULZPNRSWC4TOFZ3GS2JOMF2TAMJONVQXI5DSFZTWY33CMFWC6Y3POJSS65RSF5RXEZLEMVXHI2LBNRZS6Y3PNVYGCY3UFVZWK3LBNZ2GSYZPOJSXM33DMF2GS33OFVWGS43UOMXTCZTFGAYGINTDFU4TANDGFU2DSN3FFVRGEZJRFVQTGY3GMRRTAYRYGM3DQB6YIBIHRYMUS2CSCQSLQMKTL6Y6ZL3ICWCAUPGIGEOOWODF77V7ZJPVLGAQC2ZUP7MASXIRTIRRPOIIBKNHKZ4LHROPWBPBCYTKA3GXWIRD736HIJNQENTSFUYIPQ77BG4ZPCTXYIY"
}
```
* `id` : Unique credential identifier. We will use it in the next step to obtain the credential's
revocation status, and later to revoke the credential.
* `status`: Credential revocation status details. We will use it in the next step to obtain the
credential's revocation status.
### Obtain a credential revocation status [#obtain-a-credential-revocation-status]
Now that the credential is issued, different relying parties might be interested in discovering its
revocation status. In other words, they want to know whether or not the credential has been revoked
by the issuer. MATTR VII supports two ways of achieving this:
* Query a protected MATTR VII endpoint to get the revocation status.
* Query public MATTR VII endpoints to get the revocation status.
Again, this process looks slightly different for different credential formats.
**Protected endpoint**
Make a request of the following structure to
[retrieve the status of an mDoc](/docs/issuance/revocation/api-reference/mdocs-status-update#retrieve-mdocs-status):
```http title="Request"
GET /v2/credentials/mobile/{credentialId}/status
```
* `credentialId` : Replace with the [`id`](#prerequisites) of the mDoc you wish to check the status
for.
You can retrieve the `credentialId` by querying the
[Retrieve all users credentials data](/docs/api-reference/platform/users/getUserCredentials)
endpoint with the identifier of the user you issued the credential to, and retrieve the `id` from
the response.
*Response*
```json title="Response body"
{
"status": "valid"
}
```
* `status` : Indicates status for the mDoc. This it the expected value as we have only now issued
this mDoc.
**Public endpoint**
MATTR VII enables relying parties to obtain the status of an mDoc in a privacy preserving manner, as
the issuer has no way of knowing what specific mDoc's status the relying party is requesting.
This is achieved by retrieving a publicly available
[Status list token](/docs/issuance/revocation/overview#status-list-tokens), and then looking up a specific
mDoc's status using a reference index that is included in the mDoc itself. These Status lists are
based on Draft 14 of the IETF
[Token Status List](https://drafts.oauth.net/draft-ietf-oauth-status-list/draft-ietf-oauth-status-list.html) specification.
Make a request to the `status.status_list.uri` element from the response obtained when signing the
mDoc:
```http title="Request"
GET https://learn.vii.au01.mattr.global/v2/credentials/mobile/status-lists/f331c9be-f526-4577-bbac-ae93d6228f7a/token
```
The response will include an encoded Status list token which is a
[CBOR Web Token](https://datatracker.ietf.org/doc/html/rfc8392). Relying parties can then decode the
list and use the mDoc's `status.status_list.idx` element to locate and check the
[status](/docs/issuance/revocation/overview#status-list) of this specific mDoc.
**Validating the credential status**
You will need two physical devices to perform this validation:
* One device with the [MATTR GO Hold example app](/docs/holding/go-hold/getting-started)
and an mDoc claimed
[earlier in this tutorial](#issue-a-revocable-credential).
* One device with the
[MATTR GO Verify example app](/docs/verification/go-verify/getting-started).
Perform the following steps to validate the mDoc status:
1. Open the GO Hold example app.
2. Select the **Wallet** tab.
3. Locate the mDoc claimed earlier in this tutorial.
4. Select the **Share** button. This should display a QR code on screen.
5. Use the GO Verify example app on your second device to scan the displayed QR code.
6. Use the GO Hold example app to confirm sharing the credential.
7. The GO Verify example app should indicate succesful verification, indicating the mDoc is valid.
Not for long!!!
**Protected endpoint**
Make a request of the following structure to
[retrieve the revocation status of a CWT credential](/docs/issuance/revocation/api-reference/cwt-status-update#retrieve-cwt-credential-status):
```http title="Request"
GET /v2/credentials/compact/{id}/revocation-status
```
* `id` : Replace with the unique identifier of the credential you wish to revoke. This would be the
`id` element of the credential you issued in the previous step. It will be available as part of
any [CWT credential](/docs/concepts/cwt/data#claims) issued by a MATTR VII tenant.
*Response*
```json title="Response body"
{
"id": "bKcrxojFSuSZvI5qhKInxA",
"isRevoked": false // [!code focus]
}
```
* `isRevoked` : Indicates revocation status for the credential. Since we issued the credential with
`isRevoked` set to `false`, this it the expected value in the response.
**Public endpoint**
MATTR VII enables relying parties to obtain the revocation status of a credential in a privacy
preserving manner, as the issuer has no way of knowing what specific credential's status the relying
party is requesting.
This is achieved by retrieving a publicly available revocation status list, and then looking up a
specific credential's status using a reference index that is included in the credential itself.
These revocation lists are compliant with the
[W3C Revocation List specification](https://w3c-ccg.github.io/vc-status-rl-2020/).
Make a GET request to the `status.url` element from the response obtained when signing the
credential
```http title="Request"
GET https://learn.vii.au01.mattr.global/v2/credentials/compact/revocation-lists/887cd140-e4d7-4518-b70f-305b23778848
```
The response will include an encoded revocation list, as per the
[W3C Revocation List specification](https://w3c-ccg.github.io/vc-status-rl-2020/). Relying parties
can then decode the list and use the credential's `decoded.status.index` element to locate and check
the status of this specific credential. Check out our
[revocation check sample app](https://github.com/mattrglobal/sample-apps/blob/master/manual-revocation-check/README.md)
for a reference implementation.
**Validating the credential status**
Let's validate this status by trying to verify this credential.
Make a request of the following structure to
[verify a CWT credential](/docs/api-reference/platform/cwt-credentials-verification/verify-compact-credential):
```http title="Request"
POST /v2/credentials/compact/verify
```
```json title="Request body"
{
"payload": "CSC:/1/2KCE3IQEJB5DCMSMGZITM5QBE2QFSALWVQAXQI3ENFSDU53FMI5GYZLBOJXC45TJNEXGC5JQGEXG2YLUORZC4Z3MN5RGC3AFDJSZE7YQHIAACAACOFBW65LSONSSAQ3SMVSGK3TUNFQWYBA2NFLDPEDENZQW2ZLPIVWW2YJAJJQW4ZJAKRQXG3LBMRRW6ZDFMZEFGLRSG44HCY3FOJ2GSZTJMNQXI2LPNZHGC3LFOJLW64TLNFXGOIDBOQQEQZLJM5UHI43SMNSXE5DJMZUWGYLUNFXW4TDFOZSWYZ2MMV3GK3BAGRVGS43TOVSXETTBNVSXQGCBMR3GC3TDMVSCAU3BMZSXI6JAKRZGC2LONFXGOZTFPBYGS4TZNIZDAMRWFUYDCLJQGE5AAAIAACRAEAYDPB2WQ5DUOBZTULZPNRSWC4TOFZ3GS2JOMF2TAMJONVQXI5DSFZTWY33CMFWC6Y3POJSS65RSF5RXEZLEMVXHI2LBNRZS6Y3PNVYGCY3UF5ZGK5TPMNQXI2LPNYWWY2LTORZS6OBYG5RWIMJUGAWWKNDEG4WTINJRHAWWENZQMYWTGMBVMIZDGNZXHA4DIOAH3BAFA3FHFPDIRRKK4SM3ZDTKQSRCPRCYIA7RFUZYQI3RIGDHIGLAODJ6K2F245DTLIIKXAD35TORFQ7MVRJCIEPH6SC6NGA4HRMK76H5V6GXP66FFNX7MNYC6MYVU7ZLLXYVLXBU",
"checkRevocation": true
}
```
* `payload` : Replace with the `encoded` element from the response obtained when signing the
credential.
* `checkRevocation` : This is the property that makes this verification request check for the
credential revocation status.
*Response*
```json title="Response body"
{
"verified": true // [!code focus]
// Rest of response
}
```
* `verified` : As expected, the credential was verified. Not for long!!!
**Protected endpoint**
Make a request of the following structure to
[retrieve the revocation status of a Semantic CWT credential](/docs/issuance/revocation/api-reference/semantic-cwt-status-update#retrieve-semantic-cwt-credential-status):
```http title="Request"
GET /v2/credentials/compact-semantic/{id}/revocation-status
```
* `id` : Replace with the unique identifier of the credential you wish to revoke. This would be the
`id` element of the credential you issued in the previous step. It will be available as part of
any [Semantic CWT credential](/docs/concepts/cwt/data#claims) issued by a MATTR VII tenant.
*Response*
```json title="Response body"
{
"id": "urn:uuid:78e19496-8521-424b-8315-35fb1ecaf681",
"isRevoked": false // [!code focus]
}
```
* `isRevoked` : Indicates revocation status for the credential. Since we issued the credential with
`isRevoked` set to `false`, this it the expected value in the response.
**Public endpoint**
MATTR VII enables relying parties to obtain the revocation status of a credential in a privacy
preserving manner, as the issuer has no way of knowing what specific credential's status the relying
party is requesting.
This is achieved by retrieving a publicly available revocation status list, and then looking up a
specific credential's status using a reference index that is included in the credential itself.
These revocation lists are compliant with the
[W3C Revocation List specification](https://w3c-ccg.github.io/vc-status-rl-2020/).
Make a GET request to the `status.url` element from the response obtained when signing the
credential:
```http title="Request"
GET https://learn.vii.au01.mattr.global/v2/credentials/compact-semantic/revocation-lists/1fe00d6c-904f-497e-bbe1-a3cfdc0b8368
```
*Response*
The response includes an encoded revocation list, as per the
[W3C Revocation List specification](https://w3c-ccg.github.io/vc-status-rl-2020/). Relying parties
can then decode the list and use the credential's `decoded.status.index` element to locate and check
the status of this specific credential. Check out our
[revocation check sample app](https://github.com/mattrglobal/sample-apps/blob/master/manual-revocation-check/README.md)
for a reference implementation.
**Validating the credential status**
Let's validate this status by trying to verify this credential.
Make a request of the following structure to
[verify a Semantic CWT credential](/docs/api-reference/platform/semantic-cwt-credentials-verification/verifyCompactSemantiCredential):
```http title="Request"
POST /v2/credentials/compact-semantic/verify
```
```json title="Request body"
{
"payload": "CSS:/1/2KCE3IQEJB5DCMSMGZITM5QBE2QFSAOZUYAXQI3ENFSDU53FMI5GYZLBOJXC45TJNEXGC5JQGEXG2YLUORZC4Z3MN5RGC3AFDJSZE7YQMJ3GHI3IIBRW63TUMV4HJALYEZUHI5DQOM5C6L3XO53S45ZTFZXXEZZPGIYDCOBPMNZGKZDFNZ2GSYLMOMXXMMLEOR4XAZMCORLGK4TJMZUWCYTMMVBXEZLEMVXHI2LBNRYUG33VOJZWKICDOJSWIZLOORUWC3DRMNZGKZDFNZ2GSYLMKN2WE2TFMN2KMZDOMFWWK2SFNVWWCICUMFZW2YLEMNXWIZLGJBJS4MRXHBYWGZLSORUWM2LDMF2GS33OJZQW2ZLSK5XXE23JNZTSAYLUEBEGK2LHNB2HG4TDMVZHI2LGNFRWC5DJN5XEYZLWMVWGOTDFOZSWYIBUNJUXG43VMVZE4YLNMV4BQQLEOZQW4Y3FMQQFGYLGMV2HSICUOJQWS3TJNZTWMZLYOBUXE6LKGIYDENRNGAYS2MBRAQNGSVRXSA5AAAIAACRAEAADPB7GQ5DUOBZTULZPNRSWC4TOFZ3GS2JOMF2TAMJONVQXI5DSFZTWY33CMFWC6Y3POJSS65RSF5RXEZLEMVXHI2LBNRZS6Y3PNVYGCY3UFVZWK3LBNZ2GSYZPOJSXM33DMF2GS33OFVWGS43UOMXTCZTFGAYGINTDFU4TANDGFU2DSN3FFVRGEZJRFVQTGY3GMRRTAYRYGM3DQB6YIBIHRYMUS2CSCQSLQMKTL6Y6ZL3ICWCAUPGIGEOOWODF77V7ZJPVLGAQC2ZUP7MASXIRTIRRPOIIBKNHKZ4LHROPWBPBCYTKA3GXWIRD736HIJNQENTSFUYIPQ77BG4ZPCTXYIY",
"checkRevocation": true
}
```
* `payload` : Replace with the `encoded` element from the response obtained when signing the
credential.
* `checkRevocation` : This is the property that makes this verification request check for the
credential revocation status.
*Response*
```json title="Response body"
{
"verified": true // [!code focus]
// Rest of response
}
```
* `verified` : As expected, the credential was verified. Not for long!!!
### Revoking an issued credential [#revoking-an-issued-credential]
Next we will learn how to revoke issued credentials. The process is very similar for different
credential formats but uses different MATTR VII endpoints
Make a request of the following structure to
[revoke an mDoc](/docs/issuance/revocation/api-reference/mdocs-status-update#update-mdocs-status):
```http title="Request"
POST /v2/credentials/mobile/{credentialId}/status
```
* `credentialId` : Replace with the [`id`](#prerequisites) of the mDoc you wish to revoke.
```json title="Request body"
{
"status": "suspended"
}
```
*Response*
```json title="Response body"
{
"status": "suspended"
}
```
* `status` : This is now set to `suspended`, which means the mDoc should not be verified as valid.
Make a request of the following structure to
[revoke a CWT credential](/docs/issuance/revocation/api-reference/cwt-status-update#update-cwt-credential-status):
```http title="Request"
POST /v2/credentials/compact/{id}/revocation-status
```
* `id` : Replace with the `id` of the credential obtained earlier in this tutorial.
```json title="Request body"
{
"isRevoked": true
}
```
*Response*
```json title="Response body"
{
"id": "bKcrxojFSuSZvI5qhKInxA",
"isRevoked": true // [!code focus]
}
```
* `isRevoked` : This is now set to `true`, which means the credential will not be verified as valid.
Making a similar request with `isRevoked` set to `false` would unrevoke a
revoked credential.
Make a request of the following structure to
[revoke a Semantic CWT credential](/docs/issuance/revocation/api-reference/semantic-cwt-status-update#update-semantic-cwt-credential-status):
```http title="Request"
POST /v2/credentials/compact-semantic/{id}/revocation-status
```
* `id` : Replace with the `id` of the credential obtained earlier in this tutorial.
```json title="Request body"
{
"isRevoked": true
}
```
*Response*
```json title="Response body"
{
"id": "urn:uuid:78e19496-8521-424b-8315-35fb1ecaf681",
"isRevoked": true // [!code focus]
}
```
* `isRevoked` : This is now set to `true`, which means the credential will not be verified as valid.
Making a similar request with `isRevoked` set to `false` would unrevoke a
revoked credential.
### Attempting to verify a revoked credential [#attempting-to-verify-a-revoked-credential]
The last step will be attempting to verify the revoked credential. This step is also similar across
different credential formats, using different endpoints.
You will need two physical devices to perform this validation:
* One device with the [MATTR GO Hold example app](/docs/holding/go-hold/getting-started)
and an mDoc claimed
[earlier in this tutorial](#issue-a-revocable-credential).
* One deivce with the
[MATTR GO Verify example app](/docs/verification/go-verify/getting-started).
Perform the following steps to validate the mDoc status:
1. Open the GO Hold example app.
2. Select the **Wallet** tab.
3. Locate the mDoc claimed earlier in this tutorial.
4. Select the **Share** button. This should display a QR code on screen.
5. Use the GO Verify example app on your second device to scan the displayed QR code.
6. Use the GO Hold example app to confirm sharing the credential.
7. The GO Verify example app should fail the verification, indicating the credential was revoked by
the issuer.
Make a request of the following structure to
[verify a CWT credential](/docs/api-reference/platform/cwt-credentials-verification/verify-compact-credential):
```http title="Request"
POST /v2/credentials/compact/verify
```
```json title="Request body"
{
"payload": "CSC:/1/2KCE3IQEJB5DCMSMGZITM5QBE2QFSALWVQAXQI3ENFSDU53FMI5GYZLBOJXC45TJNEXGC5JQGEXG2YLUORZC4Z3MN5RGC3AFDJSZE7YQHIAACAACOFBW65LSONSSAQ3SMVSGK3TUNFQWYBA2NFLDPEDENZQW2ZLPIVWW2YJAJJQW4ZJAKRQXG3LBMRRW6ZDFMZEFGLRSG44HCY3FOJ2GSZTJMNQXI2LPNZHGC3LFOJLW64TLNFXGOIDBOQQEQZLJM5UHI43SMNSXE5DJMZUWGYLUNFXW4TDFOZSWYZ2MMV3GK3BAGRVGS43TOVSXETTBNVSXQGCBMR3GC3TDMVSCAU3BMZSXI6JAKRZGC2LONFXGOZTFPBYGS4TZNIZDAMRWFUYDCLJQGE5AAAIAACRAEAYDPB2WQ5DUOBZTULZPNRSWC4TOFZ3GS2JOMF2TAMJONVQXI5DSFZTWY33CMFWC6Y3POJSS65RSF5RXEZLEMVXHI2LBNRZS6Y3PNVYGCY3UF5ZGK5TPMNQXI2LPNYWWY2LTORZS6OBYG5RWIMJUGAWWKNDEG4WTINJRHAWWENZQMYWTGMBVMIZDGNZXHA4DIOAH3BAFA3FHFPDIRRKK4SM3ZDTKQSRCPRCYIA7RFUZYQI3RIGDHIGLAODJ6K2F245DTLIIKXAD35TORFQ7MVRJCIEPH6SC6NGA4HRMK76H5V6GXP66FFNX7MNYC6MYVU7ZLLXYVLXBU",
"checkRevocation": true
}
```
* `payload` : Replace with the `encoded` element from the response obtained when signing the
credential.
* `checkRevocation` : This is the property that makes this verification request check for the
credential revocation status.
*Response*
```json title="Response body"
{
"verified": false,
"reason": {
"type": "Revoked",
"message": "Credential has been revoked"
}
// Rest of response
}
```
* `verified` : As expected, the credential had failed verification.
* `reason` : Details that the credential had failed verification since it has been revoked.
Revocation lists are cached for a certain amount of time, so you might need to
wait a few minutes before verification would actually fail.
Make a request of the following structure to
[verify a Semantic CWT credential](/docs/api-reference/platform/semantic-cwt-credentials-verification/verifyCompactSemantiCredential):
```http title="Request"
POST /v2/credentials/compact-semantic/verify
```
```json title="Request body"
{
"payload": "CSS:/1/2KCE3IQEJB5DCMSMGZITM5QBE2QFSAOZUYAXQI3ENFSDU53FMI5GYZLBOJXC45TJNEXGC5JQGEXG2YLUORZC4Z3MN5RGC3AFDJSZE7YQMJ3GHI3IIBRW63TUMV4HJALYEZUHI5DQOM5C6L3XO53S45ZTFZXXEZZPGIYDCOBPMNZGKZDFNZ2GSYLMOMXXMMLEOR4XAZMCORLGK4TJMZUWCYTMMVBXEZLEMVXHI2LBNRYUG33VOJZWKICDOJSWIZLOORUWC3DRMNZGKZDFNZ2GSYLMKN2WE2TFMN2KMZDOMFWWK2SFNVWWCICUMFZW2YLEMNXWIZLGJBJS4MRXHBYWGZLSORUWM2LDMF2GS33OJZQW2ZLSK5XXE23JNZTSAYLUEBEGK2LHNB2HG4TDMVZHI2LGNFRWC5DJN5XEYZLWMVWGOTDFOZSWYIBUNJUXG43VMVZE4YLNMV4BQQLEOZQW4Y3FMQQFGYLGMV2HSICUOJQWS3TJNZTWMZLYOBUXE6LKGIYDENRNGAYS2MBRAQNGSVRXSA5AAAIAACRAEAADPB7GQ5DUOBZTULZPNRSWC4TOFZ3GS2JOMF2TAMJONVQXI5DSFZTWY33CMFWC6Y3POJSS65RSF5RXEZLEMVXHI2LBNRZS6Y3PNVYGCY3UFVZWK3LBNZ2GSYZPOJSXM33DMF2GS33OFVWGS43UOMXTCZTFGAYGINTDFU4TANDGFU2DSN3FFVRGEZJRFVQTGY3GMRRTAYRYGM3DQB6YIBIHRYMUS2CSCQSLQMKTL6Y6ZL3ICWCAUPGIGEOOWODF77V7ZJPVLGAQC2ZUP7MASXIRTIRRPOIIBKNHKZ4LHROPWBPBCYTKA3GXWIRD736HIJNQENTSFUYIPQ77BG4ZPCTXYIY",
"checkRevocation": true
}
```
* `payload` : Replace with the `encoded` element from the response obtained when signing the
credential.
* `checkRevocation` : This is the property that makes this verification request check for the
credential revocation status.
*Response*
```json title="Response body"
{
"verified": false,
"reason": {
"type": "Revoked",
"message": "Credential has been revoked"
}
// Rest of response
}
```
* `verified` : As expected, the credential had failed verification.
* `reason` : Details that the credential had failed verification since it has been revoked.
Revocation lists are cached for a certain amount of time, so you might need to
wait a few minutes before verification would actually fail.
## Summary [#summary]
In this tutorial you learned how to manage credential revocation, including:
1. How to issue a revocable credential.
2. How to check the revocation status of a credential as an Issuer and as a relying party.
3. How to revoke a credential.
You can now integrate these capabilities into your solution to support this important feature.
# API Reference
URL: /docs/issuance/users/api-reference
## Create a User [#create-a-user]
## Retrieve all Users [#retrieve-all-users]
## Retrieve a User [#retrieve-a-user]
## Update a User [#update-a-user]
## Delete a User [#delete-a-user]
## Search Users [#search-users]
## Retrieve User credentials Data [#retrieve-user-credentials-data]
# Making use of Users in MATTR VII Issuance workflows
URL: /docs/issuance/users/guide
## Overview [#overview]
This guide demonstrates how to leverage the MATTR VII Users concept throughout the credential issuance lifecycle. By properly managing user associations at each phase (before, during, and after issuance) you can maintain relationships between your external user identifiers and credentials, retrieve user-specific claims from external data sources, and trigger downstream business processes based on credential issuance events:
* [Before issuing the credential](#before-issuing-the-credential): Ensure the credential is associated with the correct user in MATTR VII.
* [During credential issuance](#during-credential-issuance):
* Retrieve user-specific claims from external data sources to populate the issued credential.
* Create dynamic, user-specific interactions as part of the issuance process.
* [After credential issuance](#after-credential-issuance): Trigger downstream business processes based on the user associated with the issued credential.
The approach differs depending on whether you're using [Pre-Authorized Code flow](/docs/issuance/pre-authorized-code/overview) or [Authorization Code flow](/docs/issuance/authorization-code/overview), as explained in each section below.
## Before Issuing the Credential [#before-issuing-the-credential]
Ensure that the credential will be associated with the correct user in MATTR VII before initiating the issuance process.
In [Authorization Code flows](/docs/issuance/authorization-code/overview) user association happens automatically based on the user identifier returned from the authentication provider.
In [Pre-authorized Code flows](/docs/issuance/pre-authorized-code/overview) you must explicitly manage user associations before creating the credential offer. This ensures that multiple credentials issued to the same individual are properly linked to a single user record.
In Pre-authorized Code flows, if you are issuing credentials to new users who do not yet have a MATTR VII user record, you can omit the user association steps below. When you create the Pre-authorized Code credential offer without specifying a `userId`, MATTR VII will automatically create a new user record when the credential offer is created and return the user ID in the response. You can later update this user record with your external identifier if needed.
### Search for an existing user [#search-for-an-existing-user]
Before creating a Pre-authorized Code flow [credential offer](/docs/issuance/credential-offer/overview), use the MATTR VII Search Users API to determine whether a user with your external identifier already exists. This prevents duplicate user records and maintains a consistent relationship between your external user identifiers and MATTR VII users.
Make a request of the following structure to search for a user by their external identifier:
```http title="Request"
POST /v1/users/search
```
```json title="Request body"
{
"claims": {
"externalUserId": "945214ad-3635-4aff-b51d-61d69a3c8eee"
}
}
```
* `externalUserId`: Replace this value with the unique identifier you use in your external system to represent the user. This could be a GUID, UUID, username, or any other stable and unique identifier.
If the search returns an empty array, no MATTR VII user currently exists with that external identifier and you can proceed to [create a new user record](#create-user).
```json title="No user found response"
{
"data": []
}
```
If a matching user is found, the response contains the user object.
```json title="User found response"
{
"data": [
{
"id": "19bf8183-a9dc-41cd-9336-1f5d19f1ae3d", // [!code focus]
"claims": {
"externalUserId": "945214ad-3635-4aff-b51d-61d69a3c8eee"
}
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwM..."
}
```
* `id`: The MATTR VII User ID. Extract and store it as you'll need it for subsequent operations.
### Create User [#create-user]
If the search returns no user, create a new user and include your external identifier in its claims.
Make a request of the following structure to create a new user:
```http title="Request"
POST /v1/users
```
```json title="Request body"
{
"claims": {
"externalUserId": "945214ad-3635-4aff-b51d-61d69a3c8eee"
}
}
```
The response includes an `id` field which represents the MATTR VII User ID. Extract and store it as you'll need it for subsequent operations:
```json title="Response"
{
"id": "19bf8183-a9dc-41cd-9336-1f5d19f1ae3d", // [!code focus]
"claims": {
"externalUserId": "945214ad-3635-4aff-b51d-61d69a3c8eee"
}
}
```
### Create a Pre-authorized Code credential offer [#create-a-pre-authorized-code-credential-offer]
When [creating the Pre-authorized Code credential offer](/docs/issuance/credential-offer/guide), include the MATTR VII User ID in the request. This ensures the issued credential is linked to the correct user record.
Make a request of the following structure to create a pre-authorized credential offer:
```http title="Request"
POST /v1/openid/offers/pre-authorized
```
```json title="Request body"
{
"credentials": [
"946c4d4a-289b-4d14-8082-41b6bf749c35"
],
"userId": "19bf8183-a9dc-41cd-9336-1f5d19f1ae3d", // [!code focus]
"claims": {},
"claimsToPersist": [],
"expiresIn": {
"minutes": 5,
"seconds": 0
}
}
```
* `userId`: Set this to the MATTR VII User ID obtained from the previous steps. Alternatively, if you know that this is a new user, you can leave this field out and MATTR VII will create a new user record when the credential offer is created and return the user ID in the response.
In the example above, no claims are included in the credential offer creation request. This is typical when using a [Claims Source](/docs/issuance/claims-source/overview) to dynamically retrieve user-specific data during issuance, as detailed in the [next section](#during-credential-issuance) of this guide.
### Authorization Code Flow [#authorization-code-flow]
In Authorization Code flows, user association is handled automatically. When a user authenticates, MATTR VII:
1. Receives the authenticated user's subject identifier from the authentication provider.
2. Stores this identifier in the `authenticationProvider.subjectId` field of the user object.
3. Checks for an existing user with a matching `authenticationProvider.subjectId`.
4. Creates a new user if no match is found, or uses the existing user if it is found.
5. Associates the credential with this user.
No manual user management is required in this flow.
## During Credential Issuance [#during-credential-issuance]
Retrieve user-specific information from external systems during the issuance process when using a [Claims source](/docs/issuance/claims-source/overview) or an [Interaction hook](/docs/issuance/authorization-code/interaction-hook/overview) (Interaction hooks are only available in Authorization Code issuance flows).
### Pre-Authorized Code Flow [#pre-authorized-code-flow]
In Pre-Authorized Code flows, include the user's unique external identifier in the credential offer by mapping it into the claims object. When you've configured a [Claims Source](/docs/issuance/claims-source/overview), MATTR VII can use this external identifier to retrieve user-specific data from your external systems and use them in the credential issuance process.
**How It Works**
1. A user initiates credential issuance via a Pre-Authorized Code flow.
2. MATTR VII identifies the associated user record using the `userId` provided in the credential offer.
3. MATTR VII extracts the `externalUserId` from the user's claims object.
4. MATTR VII calls the configured Claims Source, passing `externalUserId` as a request parameter.
5. Your Claims Source uses this identifier to look up user-specific data in your systems.
6. The retrieved claims are used in the credential issuance process according to your credential configuration's claim mapping.
### Authorization Code Flow [#authorization-code-flow-1]
In Authorization Code flows, your Claims Source or interaction hook custom component must be able to map the `authenticationProvider.subjectId` (the identifier returned from the authentication provider) into the specific user data entry in your external systems.
**How It Works**
1. A user initiates credential issuance via an Authorization Code flow and authenticates with the configured [Authentication provider](/docs/issuance/authorization-code/authentication-provider/overview).
2. MATTR VII identifies the associated user record using the `authenticationProvider.subjectId`.
3. MATTR VII calls the configured Claims Source, passing the authentication provider's subject identifier.
4. Your Claims Source maps this authentication identifier to your external user identifier.
5. Your Claims Source uses the mapped identifier to look up user-specific data.
6. The retrieved claims are used in the credential issuance process according to your credential configuration's claim mapping.
The same workflow applies when a configured interaction hook component requires user-specific data during the issuance process.
### Configuring Your Claims Source Mapping [#configuring-your-claims-source-mapping]
In your Claims Source configuration, map the user's external identifier claim to a request parameter that will be sent to your Claims Source server:
1. In the navigation panel on the left-hand side, expand the **Credential Issuance** menu.
2. Select **Claims sources**.
3. Click the **Create new** button.
4. Complete all required fields as per your Claims source configuration.
5. Paste the required mapping into the *Request parameters* field, for example:
```json title="Request parameters"
{
"externalUserId": {
"mapFrom": "claims.externalUserId"
}
}
```
6. Select **Create** to create the Claims source.
Make a request of the following structure to
[configure a new claims source](/docs/issuance/claims-source/api-reference#configure-a-claims-source):
```http title="Request"
POST /v1/claim-sources
```
```json title="Request body"
{
"name": "My Claims source",
"url": "",
"authorization": {
"type": "api-key",
"value": "supersecretapikey"
},
"requestMethod": "GET",
"requestParameters": { // [!code focus]
"userId": { // [!code focus]
"mapFrom": "claims.externalUserId" // [!code focus]
} // [!code focus]
}
}
```
* `requestParameters`: Map the user's `externalUserId` claim in MATTR VII into the `userId` request parameter sent to your Claims Source.
MATTR VII includes the mapped parameter in the request to your Claims Source. The Claims Source uses it to fetch and return user-specific claims. MATTR VII then applies these claims to populate the credential according to the credential configuration.
For more information on configuring a Claim source, refer to the [overview](/docs/issuance/claims-source/overview) page and the [tutorial](/docs/issuance/claims-source/tutorial).
## After Credential Issuance [#after-credential-issuance]
Use [Webhooks](/docs/platform-management/webhooks-overview) to trigger downstream business processes and workflows based on credential issuance events. The webhook payload includes information about the user the credential was issued to, which differs based on the issuance flow:
* In Pre-Authorized Code flows, the webhook payload includes the external identifier [you provided](#before-issuing-the-credential) as part of the credential offer. This allows you to directly correlate the issuance event with your external systems.
* In Authorization Code flows, the webhook payload includes the `authenticationProvider.subjectId` from the authentication provider. You'll need to map this identifier to the corresponding identifiers in your downstream business systems and processes.
### Retrieving User Credentials metadata [#retrieving-user-credentials-metadata]
Another useful operation after credential issuance is retrieving all credentials associated with a specific user. This can be used to audit issued credentials, manage revocations, or analyze user activity.
#### Search for a user [#search-for-a-user]
Make a request of the following structure to search for a user by their external identifier:
```http title="Request"
POST /v1/users/search
```
```json title="Request body"
{
"claims": {
"externalUserId": "945214ad-3635-4aff-b51d-61d69a3c8eee"
}
}
```
* `externalUserId`: Replace this value with the unique identifier you use in your external system to represent the user. This could be a GUID, username, email, or any other stable and unique identifier.
#### Retrieve User Credentials metadata [#retrieve-user-credentials-metadata]
Make a request of the following structure to retrieve all metadata for credentials issued to this user:
```http title="Request"
GET /v1/users/{userId}/credentials
```
* `userId`: Set this to the MATTR VII User ID obtained from the previous step.
The response returns a `data` array. Each element contains metadata for a credential issued to the user. Fields vary by credential format. See the [API Reference](/docs/api-reference/platform/users/getUserCredentials) for full schema details.
You can now use this metadata to trigger any required downstream business processes.
### Implementing Webhook Handlers [#implementing-webhook-handlers]
MATTR VII can send webhook notifications for successful credential issuance events. These webhook payloads include the user ID and claims, allowing you to trigger business workflows in your downstream applications.
#### Configure a Webhook [#configure-a-webhook]
[Configure a webhook](/docs/platform-management/webhooks-guide) for OID4VCI issuance events in your MATTR VII tenant. This will ensure you receive notifications whenever credentials are successfully issued.
#### Receive Webhook Notification [#receive-webhook-notification]
When a credential is issued, MATTR VII sends a webhook notification containing the user information:
```json title="Webhook Payload Example"
{
"format": "mso_mdoc",
"userId": "19bf8183-a9dc-41cd-9336-1f5d19f1ae3d", // [!code focus]
"credentialProfile": "mobile",
"credentialConfigurationId": "1d8c7ad7-84ce-4519-8365-7af986e4ee0e",
"credentialId": "26b902fc-c1bd-4178-a228-4623b1e3ebee",
"userClaims": {
"externalUserId": "945214ad-3635-4aff-b51d-61d69a3c8eee" // [!code focus] // Pre-Auth flow
},
"credential": "{base64url_encoded_credential}"
}
```
* **Pre-Authorized Code flow**: The `userClaims` object contains the external identifier you provided when creating the credential offer.
* **Authorization Code flow**: The user information is available through the `userId` field, which you can use to retrieve the full user object (including `authenticationProvider.subjectId`) via the Users API if needed for mapping to your external systems.
#### Process Webhook Payload and Trigger Workflows [#process-webhook-payload-and-trigger-workflows]
Extract the user information from the webhook payload:
* **Pre-Authorized Code flow**: Use `userClaims.externalUserId` to directly identify the user in your systems.
* **Authorization Code flow**: Use `userId` to retrieve the user record and map `authenticationProvider.subjectId` to your external systems.
You can now use this information to:
* Update records in your backend systems
* Trigger notifications to users via email, SMS, or push notifications
* Update billing systems or usage tracking
* Initiate downstream processes such as account provisioning
* Log credential issuance events for audit purposes
* Trigger approval workflows or compliance checks
Refer to the [Webhooks guide](/docs/platform-management/webhooks-guide) to learn more about handling webhook events in MATTR VII.
## Best Practices [#best-practices]
1. **Avoid duplicates**: Use existing user records whenever possible instead of creating new ones. When in doubt, search for the user first.
2. **Store User IDs Securely**: Maintain a secure mapping between your external user identifiers and MATTR VII user IDs in your systems.
3. **Include User ID in Pre-Auth Offers**: Always include the `userId` parameter when creating pre-authorized credential offers to ensure proper user association.
4. **Use Consistent External Identifiers**: Use stable, unique identifiers from your systems (such as GUIDs or user IDs) rather than potentially changing values like email addresses.
5. **Implement Idempotent Webhook Handlers**: Design your webhook handlers to be idempotent, as webhook notifications may be delivered multiple times.
6. **Monitor User Creation Patterns**: Regularly review user creation patterns to ensure your integration is working as expected and not creating duplicate users.
7. **Handle Race Conditions**: In high-concurrency scenarios, implement appropriate locking or conflict resolution strategies when searching for and creating users.
# Users
URL: /docs/issuance/users/overview
## Overview [#overview]
Users in MATTR VII are entities that represent credential holders within the issuance system. They are created and managed automatically as part of OpenID for Verifiable Credential Issuance (OID4VCI) flows, providing a link between credentials issued by MATTR VII and the individuals or entities that hold them.
Every credential that is issued through MATTR VII via OID4VCI is associated with a specific user. This association enables issuers to maintain a relationship between the MATTR VII user and external systems, supporting various business workflows and integrations.
## Possible use cases [#possible-use-cases]
Associating credentials with users in MATTR VII provides several important capabilities:
* **Credential Tracking**: Keeping an organized record regarding who has been issued which credentials.
* **External Identity Mapping**: Link MATTR VII users with external user identifiers in your existing systems.
* **Claims Retrieval**: Retrieve claims from external data stores ([Claims Sources](/docs/issuance/claims-source/overview)) for specific users during issuance.
* **Business Workflow Integration**: Trigger different business processes when credentials are issued to specific users.
* **System Updates**: Update external datastores when credentials are issued.
* **Notifications and Billing**: Trigger user-specific notifications or billing operations based on credential issuance events.
## User Object Structure [#user-object-structure]
A user object in MATTR VII contains the following elements:
```json title="Example User Object"
{
"id": "0c3fad74-a8df-4a2d-8e75-f2d356b413ba",
"claims": {
"externalUserId": "0c3fad74-a8df-4a2d-8e75-f2d356b413ba"
},
"authenticationProvider": {
"providerId": "41458e5a-9092-40b7-9a26-d4eb43c5792f",
"url": "https://example-university.au.auth0.com",
"subjectId": "example-university-oauth2|123456789"
}
}
```
* `id`: A unique identifier (UUID) for the user within MATTR VII. This ID is used to reference the user in API operations and is associated with all credentials issued to this user.
* `claims`: An object containing custom claims about the user. This object is dynamic and can include any key-value pairs relevant to the user. A common use case is to include an `externalUserId` claim that maps the MATTR VII user to an identifier in an external system. As the contents of the `claims` object are persistent, it is recommended to avoid including sensitive information here when creating users.
* `authenticationProvider`: Information about the authentication provider used to verify the user's identity (only applicable for users who have claimed credentials issued via OID4VCI [Authorization code flows](/docs/issuance/authorization-code/overview)):
* `providerId`: The unique identifier of the configured authentication provider in MATTR VII.
* `url`: The URL of the authentication provider.
* `subjectId`: The unique identifier for this user within the authentication provider's system.
## User Creation by Issuance Workflow [#user-creation-by-issuance-workflow]
User creation in MATTR VII differs based on the OID4VCI flow being used. Understanding these differences is important for properly managing users in your implementation.
### Authorization Code Flow [#authorization-code-flow]
In the Authorization Code flow, the user's identity is only known after they interact with the credential offer and complete authentication. This workflow follows these rules:
* You cannot and should not include a `userId` in the credential offer. The system has no way to know the user's identity until they authenticate.
* The `userId` is automatically allocated based on the user's unique identifier with the authentication provider (`authenticationProvider.subjectId`):
* The user authenticates with the configured authentication provider.
* MATTR VII receives the `subjectId` from the authentication provider.
* The system checks for an existing user with a matching `authenticationProvider.subjectId`.
* If no matching user is found, a new user is created.
* The credential is then associated with the existing or newly created user.
This ensures that all credentials issued to the same authenticated user are associated with a single user record in MATTR VII.
### Pre-Authorized Code Flow [#pre-authorized-code-flow]
In the Pre-Authorized Code flow, the user's identity should be known before they interact with the credential offer. This allows you to control user association explicitly.
The `userId` parameter in the API request to create a pre-authorized credential offer is optional, but its usage significantly affects user management.
* If you do **not** include a `userId` when creating the credential offer:
* There is no way for MATTR VII to associate multiple credentials with the same user.
* A new user record is created immediately when the credential offer is created, even if the credential is never claimed.
* If you do **include** a `userId` when creating the credential offer:
* If a user already exists with this `id`, no new user is created, and the credential is associated with the existing user.
* If no user exists with this `id`, a 400 Bad Request error is returned, indicating that the specified user does not exist.
For Pre-Authorized Code flow, it is recommended to always include a `userId` in your credential offer when you want to associate multiple credentials with the same user. This gives you full control over user management and enables proper tracking of credentials issued to specific individuals.
## Deleting users [#deleting-users]
You can delete a user from your tenant using the [Delete a user](/docs/api-reference/platform/users/deleteUser) API.
Deleting a user is irreversible. When a user is deleted, all of their data is removed, and any credential previously issued to them is no longer valid.
Because deletion invalidates any credentials previously issued to the user, treat it as a way to revoke a holder's credentials along with removing their data. If you only need to remove specific persisted claims while keeping issued credentials valid, [update the user](/docs/api-reference/platform/users/updateUser) instead of deleting the user.
# API Reference
URL: /docs/platform-management/analytics/api-reference
## Retrieve Analytic events [#retrieve-analytic-events]
# How to query analytic events
URL: /docs/platform-management/analytics/guide
Monitoring analytics can be performed either via the MATTR Portal or the MATTR VII APIs.
1. Expand the **Platform Management** menu in the navigation panel on the left-hand side.
2. Select **Monitoring**.
3. Use the drop-down list on the upper-left corner to select the tenant you wish to query analytic
events for.
4. Query the select tenant analytics database for events using any combination of the following
query methods:
* Use the *Event ID* or *Request ID* text box to query the database for events that match
unique identifiers of specific events or requests you are interested in.
* Use the *Events* drop-down list to query the database for events of a specific category. You
can also filter for specific types of events within each category.
* Use the *Requestor Type* drop-down list to query the database for events generated by specific users, clients or systems.
* Use the *Time* drop-down list to query the database for events within a given time frame. You
can use any of the existing filters (e.g. Last minute, Last 5 minutes, etc.) or manually set
custom `FROM` and `TO` criteria. Any filters you apply will update the list in real-time.
Refer to the [Events
registry](https://api-reference-sdk.mattr.global/event-registry/latest/index.html) for an
inclusive list of available event categories and types. Alternatively, each endpoint in our [API
Reference](/docs/api-reference) indicates what event types can be generated when it is called.
Make a request of the following structure to
[query MATTR VII analytic events](/docs/platform-management/analytics/api-reference#retrieve-analytic-events):
```http title="Request"
GET /v1/events?requestIds=0QdK3jkDSPbOu1Sx9K2lM9
```
This request queries the tenant for all analytic events that match the provided `requestIds`. You
can append any of the available [query parameters](#query-parameters) to your request.
*Response*
```http title="Response body"
{
"data": [
{
"id": "f570ff36-9035-439c-8e25-dc0cd67b4b4a",
"type": "DID_RETRIEVE_LIST_SUCCESS",
"timestamp": "2023-08-20T22:32:59.151Z",
"category": "did",
"requestId": "0QdK3jkDSPbOu1Sx9K2lM9",
"metadata": {
"nextCursor": "Y3JlYXRlZEF0PTIwMjMtMDctMzFUMDAlM0EyMSUzQTMwLjUyN1omaWQ9Yzk3NmQ0YmEtNjE2OC00MTkyLWE4YjAtNjE3NzNiZmNhM2M3",
"entriesCount": 8
},
"data": null
},
{
"id": "d7a38f9a-374f-4e4d-9cc0-42149d61540a",
"type": "DID_RETRIEVE_LIST_START",
"timestamp": "2023-08-20T22:32:59.148Z",
"category": "did",
"requestId": "0QdK3jkDSPbOu1Sx9K2lM9",
"metadata": {
"limit": 100
},
"data": null
}
],
"nextCursor": "dGltZXN0YW1wPTIwMjMtMDgtMjBUMjIlM0EzMiUzQTU5LjE0OFomaWQ9ZDdhMzhmOWEtMzc0Zi00ZTRkLTljYzAtNDIxNDlkNjE1NDBh"
}
```
* `data` : This array includes all the events that matched the query parameters. The example
response includes two events that were part of a DID retrieval request - one for the start of the
request, and one for the successful completion. Each event includes the following parameters:
* `id` : Event identifier.
* `type` : Event type.
* `timestamp` : Event creation date and time.
* `category` : Event category.
* `requestId` : Request identifier.
* `metadata` : Available metadata will vary based on Event type and logging level.
* `data` : Available data will vary based on Event type and logging level.
* `nextCursor` : This element is used for pagination the response. Refer to our
[API Reference](/docs/api-reference#pagination) for more information.
## Query parameters [#query-parameters]
* All parameters support comma separated lists.
* All query parameters are optional. If no parameters are provided, the most recent 100 events are
returned by default.
* Use the `limit` and `cursor` parameters in your request to control the response
[pagination](/docs/api-reference#pagination).
* The following request parameters are available:
* `ids` : Query by event IDs. These can be retrieved from the event details. *Example*:
`ids=e4c387e7-3e63-40f4-9a38-062aaae9ee50`.
* `requestIds` : Query by request IDs. These can be retrieved from the event details. The
response will include all the individual events that are part of the queried request.
*Example*: `requestIds=0QdK3jkDSPbOu1Sx9K2lM9`.
* `categories` : Query by event categories. Every category includes several event `types`. When
both the categories and types parameters are provided an `OR` logic is applied, so the
response will return all events that match either. Refer to the
[Events registry](https://api-reference-sdk.mattr.global/event-registry/latest/index.html) for
an inclusive list. *Example*: `categories=credential_compact`.
* `types` : Query by event types. Each event type is part of a `category`. When both the
categories and types parameters are provided an `OR` logic is applied, so the response will
return all events that match either. Refer to the
[Events registry](https://api-reference-sdk.mattr.global/event-registry/latest/index.html) for
an inclusive list. The [API Reference](/docs/api-reference) also details event types that are
generated by specific endpoints. *Example*: `categories=CREDENTIAL_COMPACT_SIGN_START`.
* `dateFrom` / `dateTo` : Query by event start and/or end date and time, in ISO-8601 format.
*Examples*: `dateFrom=2023-06-01T02:45:44.087Z` / `dateTo=2023-06-30T23:59:59.999Z`.
* `clientIds` : Query by client IDs. These can be retrieved from the event details. The response
will include all the individual events that are part of the queried client. *Example*:
`clientIds=1f3a9c2b-7d4e-4c1f-9a6b-8d2e5f1a4b3c`.
* `managementUserIds` : Query by management user IDs. These can be retrieved from the event
details. The response will include all the individual events that are part of the queried
management user. *Example*: `managementUserIds=ea691ed4-90ff-4be2-bd85-f2c74efa72c3`.
# Analytics
URL: /docs/platform-management/analytics/overview
The MATTR VII Analytics APIs allow viewing analytic data from tenant interactions in the form of
events. These events form a database that can be seen as a history of all the requests made by a
tenant to the platform. The events can be generated with different ranges of data, ranging from no
data up to a full response.
The Analytics service uses a standard publisher-subscriber architecture. MATTR VII services create
and publish events that are pushed to a queue. The Analytics service then consumes these events and
stores them in a database.
You can [query](/docs/platform-management/analytics/guide) the database for events and retrieve their metadata and data using either
direct API requests or the MATTR Portal.
The [Events registry](https://api-reference-sdk.mattr.global/event-registry/latest/index.html) is a
comprehensive collection of analytic events generated by the MATTR VII platform. In addition, our
[API Reference](/docs/api-reference) indicates what event types can be generated when calling each
endpoint.
## Events structure [#events-structure]
The structure of MATTR VII analytic events depends on the following:
* [**Event type**](#event-types): Different event types generate different event payloads. Refer to
the [events registry](#events-registry) for an inclusive list.
* [**Event sanitization**](#sanitised-events): MATTR VII analytics can be configured to three
different logging levels, and so each event has three possible corresponding payload versions:
* Level 1: Metadata only.
* Level 2: Non-sensitive data.
* Level 3: Full event data.
* **Event version**: When new versions of events are introduced (for example as a result of a change
to an endpoint response payload structure), different versions of the event could have different
payload structures.
### Event types [#event-types]
MATTR VII events are usually generated in one of the following scenarios:
* `START` events are generated when an operation starts.
* `SUCCESS` events are generated following `START` events when the operation succeeds.
* `FAIL` events are generated following `START` events when the operation fails.
For example, when making a request to
[sign a CWT credential](/docs/issuance/direct-issuance-api-reference/cwt-issuance#sign-a-cwt-credential),
the following events might be generated:
* When a sign operation starts, a `CREDENTIAL_COMPACT_SIGN_START` event is generated.
* If the sign operation succeeds, a `CREDENTIAL_COMPACT_SIGN_SUCCESS` event is generated.
* If the sign operation fails, a `CREDENTIAL_COMPACT_SIGN_SUCCESS` event is generated.
Each event type results in a different events payload structure.
#### `START` and `SUCCESS` events [#start-and-success-events]
`START` and `SUCCESS` events return payloads of the following structure:
```json title="START and SUCCESS events payload structure"
{
"id": "string",
"type": "literal string",
"category": "string",
"timestamp": "Long",
"version": "string",
"tenantId": "string",
"requestId": "string",
"clientIds": "string",
"managementUserIds": "string",
"requestIp": "string",
"data": "(ServiceDtoInput | ServiceDtoOutput)"
}
```
* `id` : Unique event identifier.
* `type` : Event type. This might affect the structure of the payload. Refer to the
[events registry](#events-registry) for an inclusive list.
* `category` : Event category. Refer to the [events registry](#events-registry) for an inclusive
list.
* `timestamp` : Event start time, in ISO-8601 format.
* `version` : Event version. This might affect the structure of the payload.
* `tenantId` : Unique identifier of the tenant the event was generated for.
* `requestId` : Unique identifier of the request the event is part of.
* `clientIds` : Unique identifier of the client who initiated the request.
* `managementUserIds` : Unique identifier of the management user who initiated the request.
* `requestIp` : IP address from which the request was made.
* `data` : The request (for `START` events) or response (for `SUCCESS` events) body. The structure
of this Data Transfer Object (DTO) would differ based on the endpoint which generated the event.
Refer to the [events registry](#events-registry) to inspect the structure of different event
types.
##### `START` and `SUCCESS` events exceptions [#start-and-success-events-exceptions]
* Events generated by a `list` operation:
* `START` events: The `data` object would contain the `cursor`, `limit` and `id` (when
applicable) parameters. Refer to [Pagination](/docs/api-reference#pagination) for more
information.
* `SUCCESS` events: The `data` object would contain the number of entries in the response.
* Events with a large binary DTO: The data object includes either derived data, extracted meaningful
data, or a reference to the data source.
#### `FAIL` events [#fail-events]
`FAIL` events return payloads of the following structure:
```json title="FAIL events payload structure"
{
"id": "string",
"type": "literal string",
"category": "string",
"timestamp": "Long",
"version": "string",
"tenantId": "string",
"requestId": "string",
"clientIds": "string",
"managementUserIds": "string",
"requestIp": "string",
"data": {
"error": {
"type": "string",
"message": "string"
}
}
}
```
* `id` : Unique event identifier.
* `type` : Event type. This might affect the structure of the payload. Refer to the
[events registry](#events-registry) for an inclusive list.
* `category` : Event category. Refer to the [events registry](#events-registry) for an inclusive
list.
* `timestamp` : Event start time, in ISO-8601 format.
* `version` : Event version. This might affect the structure of the payload.
* `tenantId` : Unique identifier of the tenant the event was generated for.
* `requestId` : Unique identifier of the request the event is part of.
* `clientIds` : Unique identifier of the client who initiated the request.
* `managementUserIds` : Unique identifier of the management user who initiated the request.
* `requestIp` : IP address from which the request was made.
* `data` :
* `error` :
* `type` : Error type as defined by MATTR VII. Note that if the error is due to an
exception, type will be `unknown`.
* `message` : Error message as defined by MATTR VII.
### Sanitized events [#sanitized-events]
When events contain sensitive information, sanitizing them before they are logged or processed helps
in removing or anonymising this data, thus preserving privacy.
Sanitized versions of MATTR VII analytic events can be stripped of all data, or just sensitive data.
Stripped data is configured per end-points depending on the information included in the event body.
## Events registry [#events-registry]
The [Events registry](https://api-reference-sdk.mattr.global/event-registry/latest/index.html) is a
comprehensive collection of analytic events generated by the MATTR VII platform. Events are grouped
by the service that generates them, which corresponds to the event `category`.
Two Events registry are available for the following APIs:
* [MATTR VII API](https://api-reference-sdk.mattr.global/event-registry/latest/index.html)
* [Management API](https://api-reference-sdk.mattr.global/event-registry-management/latest/index.html)
In each registry the following information is available for each event:
* Event `type`.
* Event structure in the different logging levels:
* `Level 1 - Metadata`.
* `Level 2 - Non-sensitive data`.
* `Level 3 - Full event`.
* Event properties:
* Properties tagged with `Sanitization Level DATA` are removed from the event structure when
sanitization level is set to `Level 1 - Metadata`.
* Properties tagged with `Sanitization Level PII` are removed from the event structure when
sanitization level is set to `Level 2 - Non-sensitive data`.
# Management API
URL: /docs/platform-management/management-api/overview
## Overview [#overview]
The Management API offers a set of actions to assist system admins to manage the MATTR VII tenants they own. These
APIs provide programmatic access to common platform management tasks, including:
* Retrieving available environments.
* Creating and managing tenants.
* Creating and managing clients for accessing tenants.
* Managing [Role Based Access Control (RBAC)](/docs/platform-management/access-control) for users
and clients interacting with tenants.
Similar capabilities are available via the [MATTR Portal](/docs/platform-management/portal).
The Management APIs use machine-to-machine authentication through its own credentials, which are
different from the client credentials used to access specific MATTR VII tenants.
## Getting started [#getting-started]
### Obtaining a Management API access token [#obtaining-a-management-api-access-token]
Before you can make any API requests to manage your MATTR VII tenants, you must complete
authentication by obtaining a management API access token from our authentication provider.
Use your management API client credentials to make a request of the following structure:
```http title="Request"
POST https://auth.manage.au01.mattr.global/oauth/token
```
```json title="Request body"
{
"client_id": "F5qae****************************",
"client_secret": "Wzc8J**********************************************************",
"audience": "https://manage.au01.mattr.global",
"grant_type": "client_credentials"
}
```
* `client_id` : Replace with the `client_id` value from your management API client credentials.
* `client_secret` : Replace with the `client_secret` value from your management API client
credentials.
* `audience` : Always use `https://manage.au01.mattr.global` as a static value, regardless of your
specific management API client credentials.
* `grant_type` : Always use `client_credentials` as a static value, regardless of your specific
management API client credentials.
These are not the same `client_id` and `client_secret` you were provided for accessing other
MATTR VII APIs, but rather unique credentials for accessing the Management APIs. If you have not
received these credentials or have any questions, please [contact
us](mailto:dev-support@mattr.global) before proceeding.
*Response*
```json title="Response body"
{
"access_token": "eyJhb********************************************************************", // [!code focus]
"expires_in": 14400,
"token_type": "Bearer"
}
```
The returned `access_token` must be used as a bearer token for all subsequent requests to
any of the protected MATTR VII Management API endpoints, including the next step to
create a new tenant.
### Creating a tenant [#creating-a-tenant]
Use the returned `access_token` must be used as a bearer token for all requests to the Management API in
the next steps to make a request of the following structure to
[create a new tenant](/docs/api-reference/management/tenants/createTenant):
```http title="Request"
POST https://manage.au01.mattr.global/v1/tenants
```
```json title="Request body"
{
"name": "My first tenant",
"subdomain": "my-first-tenant",
"environmentId": "fa605282-0223-4ae0-831d-af368bc39a55"
}
```
* `name` : The name that will be used to identify this tenant.
* `subdomain` : The subdomain that will be used to access this tenant.
* `environmentId` : The unique identifier of the environment where the new tenant will be created.
You can make a request to
[retrieve all environments](/docs/api-reference/management/environments/getEnvironments) you have
access to and use the `id` value from the response as the `environmentId`.
*Response*
```json title="Response body"
{
"id": "6facbcef-66cd-4a06-89e3-e44a4fc12000", // [!code highlight]
"name": "My first tenant", // [!code highlight]
"subdomain": "my-first-tenant.vii.au01.mattr.global",
"environment": { // [!code highlight]
"id": "fa605282-0223-4ae0-831d-af368bc39a55",
"name": "Public Australia Sydney",
"domain": "vii.a01.mattr.global",
"deploymentModel": "public",
"authorizationServerDomain": "auth.manage.au01.mattr.global",
"region": {
"id": "0fd6ce12-a983-41d0-aca8-03e1bb6f6000",
"name": "au01",
"displayName": "Sydney, Australia"
}
},
"client": { // [!code highlight]
"clientId": "MjQx108p***************FlwJQjy",
"clientSecret": "NanfSkVr**********************PfD3zJ"
}
}
```
* `id` : Globally unique tenant identifier.
* `name` : As provided in the request.
* `subdomain` : The tenant URL, constructed with the `subdomain` value provided in the request.
* `environment` : Indicates data for the environment in which the new tenant was created.
* `client` : Indicates the `clientId` and `clientSecret` for the default client created for this
tenant. This client is assigned an
[Admin](/docs/platform-management/access-control#tenant-admin-permissions) role by default,
meaning it has access to all endpoints in the tenant.
Once the tenant is created you can interact with it as detailed in the Portal [getting started](/docs/platform-management/portal#interacting-with-the-tenant) guide.
Inviting users to a tenant is only supported for Portal users that are members of the tenant. The machine-to-machine (M2M) client used to access the Management API cannot invite users. Calling the [invite a tenant member](/docs/api-reference/management/members/inviteTenantMember) endpoint with an M2M client token returns a `404 Resource Not Found` response, even when the client holds an `admin` role.
A tenant created with an M2M client has no Portal members, so no one can be invited to it until a Portal user is a member. To manage tenant membership, create the tenant from the [MATTR Portal](/docs/platform-management/portal#inviting-users) instead. The Portal user that creates the tenant becomes a member automatically and can then invite additional users, either from the Portal UI or by calling the invitation endpoint with that Portal user's access token.
# How to create verifier certificates
URL: /docs/verification/certificates/guide
Verifiers request credentials for verification by sending verification requests to credential
holders. During this process, holders can confirm the verifier’s identity using a
[chain of trust](/docs/concepts/chain-of-trust), which links the verification request to the verifier through a
series of certificates. MATTR VII supports both managed and unmanaged (external) verifier
certificates, giving verifiers flexibility in how they manage their certificate infrastructure.
* With *managed* verifier certificates, MATTR VII provisions and maintains the Verifier root CA and Verification Request Signer Certificates (VRSCs) for you. If no managed Verifier root CA exists when you create the first [verifier application](/docs/verification/remote-verification-api-reference/verifier-applications) in a tenant, one is created automatically. The first time that application creates a verification request, MATTR VII also creates a Verification Request Signer (and its certificate) and uses it to sign the request.
* With *unmanaged* verifier certificates, the verifier supplies and maintains the full certificate chain: generating and protecting the Verifier root CA, issuing and rotating Verification Request Signer Certificates (VRSCs), and uploading the root CA and each VRSC to MATTR VII. See the [verifier chain of trust](/docs/concepts/chain-of-trust#external-certificates) for details.
The following guide describes how to use MATTR VII to configure a verification solution using
*unmanaged* (external) verifier certificates.
### Generate a self-signed root certificate (Verifier root CA) [#generate-a-self-signed-root-certificate-verifier-root-ca]
Use your preferred cryptographic library or tool to generate a self-signed root certificate
(Verifier root CA certificate). This certificate will later be used to sign the Verification Request
Signer Certificates (VRSCs). Ensure it meets the requirements specified in the
[certificate requirements](/docs/verification/certificates/overview#certificate-requirements)
section.
When using unmanaged (external) certificates, the DTS provider assumes full responsibility for the
secure management of the uploaded root certificates and all subordinate certificates. This includes
ensuring the protection, proper issuance, and timely revocation of certificates under the uploaded
root, as MATTR VII does not manage or monitor these certificates on the issuer's behalf.
### Register the external Verifier root CA certificate with MATTR VII [#register-the-external-verifier-root-ca-certificate-with-mattr-vii]
Make a request of the following structure to
[create an unmanaged Verifier root CA](/docs/verification/certificates/api-reference/verifier-root-ca-certificates#create-a-verifier-root-ca-certificate):
```http filename:"Request"
POST /v2/presentations/certificates/ca
```
```json filename:"Request body"
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICDjCCAbSgAwIBAgIKdeZsA5NPKimuAzAKBggqhkjOPQQDAjAiMSAwCQYDVQQG\r\nEwJOWjATBgNVBAMTDEV4YW1wbGUgSUFDQTAeFw0yMzA5MTEyMzM0MjJaFw0zMzA5\r\nMDgyMzM0MjJaMCIxIDAJBgNVBAYTAk5aMBMGA1UEAxMMRXhhbXBsZSBJQUNBMFkw\r\nEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBbK7JKKFMWuu8kHQK2qaML+MQ0Ykk3Qg\r\n/p3TC6lQKvYJozPSpLXbJQIzMPq9u/dG+j4vq1iX/G/jFIwfiEiKEqOB0TCBzjAS\r\nBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIABjAdBgNVHQ4EFgQU9zTh\r\nKsqFxAgRJDDGW1au+ewJK6owHgYDVR0SBBcwFYYTaHR0cHM6Ly9leGFtcGxlLmNv\r\nbTBpBgNVHR8EYjBgMF6gXKBahlhodHRwczovL2V4YW1wbGUuY29tL3YyL2NyZWRl\r\nbnRpYWxzL21vYmlsZS9pYWNhcy8yZTg5YzE1Ni0zMWQ1LTQ3ODMtYmQ1OS05MDU1\r\nYjVmOGU3ZDIvY3JsMAoGCCqGSM49BAMCA0gAMEUCIQDD+eU8iOsYYC0v41L94fhF\r\nZ0brPo4gx2aRxrhE3NLFpwIgIgHCPBXJ+JICJg3K7dEsr153So4SEZzAA1rRn4eF\r\nvkM=\r\n-----END CERTIFICATE-----\r\n"
}
```
* `certificatePem` : This required parameter contains the PEM-encoded Verifier root CA certificate.
The certificate must meet the following requirements:
* Valid
* Not expired
* Compliant with MATTR VII's
[certificate requirements](/docs/verification/certificates/overview#certificate-requirements).
The response will include an `id` property, which is a unique identifier for the unmanaged Verifier
root CA. This identifier will be used in subsequent operations to reference this unmanaged Verifier
root CA.
### Create a Verification Request Signer [#create-a-verification-request-signer]
Make a request of the following structure to
[create a Verification Request Signer](/docs/verification/certificates/api-reference/verification-request-signers#create-a-verification-request-signer)
that references the unmanaged Verifier root CA:
```http filename:"Request"
POST /v2/presentations/certificates/verifier-signers
```
```json filename:"Request body"
{
"caId": "080c670a-2e90-4023-b79f-b706e55e9bc6"
}
```
* `caId` : Replace with the `id` value obtained when you created the unmanaged Verifier root CA in
the previous step. Attempts to provide a managed Verifier root CA identifier for manual
Verification Request Signer creation will result in an error.
The response will include two properties which you will use later in this guide:
* `id` : The unique identifier for the Verification Request Signer. This identifier will be used in
subsequent operations to reference this Verification Request Signer.
* `csrPem` : The X.509 Certificate Signing Request (CSR) in PEM format. You will use this CSR to
generate a valid Verification Request Signer Certificate (VSC) in the next step.
### Generate and sign the Verification Request Signer Certificate (VRSC) [#generate-and-sign-the-verification-request-signer-certificate-vrsc]
Use your preferred cryptographic library or tool to generate and sign a Verification Request Signer
Certificate (VRSC) using the CSR provided in the response from the previous step. Refer to the
[certificate requirements](/docs/verification/certificates/overview#certificate-requirements)
section in the external Verifier certificates documentation for details on how to structure a valid
VRSC.
### Associate the VRSC with the Verification Request Signer [#associate-the-vrsc-with-the-verification-request-signer]
Make a request of the following structure to
[update the Verification Request Signer](/docs/verification/certificates/api-reference/verification-request-signers#update-a-verification-request-signer)
to activate and associate it with the generated VRSC:
```http filename:"Request"
PUT /v2/presentations/certificates/verifier-signers/{verifierSignerId}
```
* `verifierSignerId` : Replace with the `id` value obtained when you created the Verification
Request Signer in the previous step.
```json filename:"Request body"
{
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICbzCCAhSgAwIBAgIKfS7sskyJEh+DOzAKBggqhkjOPQQDAjAiMSAwCQYDVQQG\r\nEwJOWjATBgNVBAMTDEV4YW1wbGUgSUFDQTAeFw0yMzA5MTEyMzM0MjJaFw0yNDA5\r\nMTAyMzM0MjJaMDExLzAJBgNVBAYTAk5aMCIGA1UEAxMbZXhhbXBsZS5jb20gRG9j\r\ndW1lbnQgU2lnbmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7fa+jv9zCtHQ\r\nmKn7o1dS6lBHD5thlhPqjlx7qEfqy8Im9AcQJDal2sr/fUxhHwf/G4ublS7AL04U\r\n73dzr/ozxaOCASEwggEdMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFLdNNPTmPxt0\r\nLqvlZnV/QL86MXOxMB8GA1UdIwQYMBaAFPc04SrKhcQIESQwxltWrvnsCSuqMA4G\r\nA1UdDwEB/wQEAwIAgDAeBgNVHREEFzAVhhNodHRwczovL2V4YW1wbGUuY29tMB4G\r\nA1UdEgQXMBWGE2h0dHBzOi8vZXhhbXBsZS5jb20waQYDVR0fBGIwYDBeoFygWoZY\r\naHR0cHM6Ly9leGFtcGxlLmNvbS92Mi9jcmVkZW50aWFscy9tb2JpbGUvaWFjYXMv\r\nMmU4OWMxNTYtMzFkNS00NzgzLWJkNTktOTA1NWI1ZjhlN2QyL2NybDASBgNVHSUE\r\nCzAJBgcogYxdBQECMAoGCCqGSM49BAMCA0kAMEYCIQCfgn6+QoNfDVelJANl+Jp9\r\ncq7X9paZylfnI6UGr1FM6gIhAIzhiyclDa8+/ZSRfu7KfgGrNRaJ8YQ6vevskJls\r\nIavC\r\n-----END CERTIFICATE-----\r\n"
}
```
* `active` : This required boolean indicates whether the Verification Request Signer is active or
not. Can only be set to `true` when a `certificatePem` is provided. Only active Verification
Request Signers can be used to sign verification requests.
* `certificatePem` : This required parameter contains the PEM-encoded VRSC created in the previous
step.
### Activate the Verifier root CA [#activate-the-verifier-root-ca]
Make a request of the following structure to
[update the unmanaged Verifier root CA](/docs/verification/certificates/api-reference/verifier-root-ca-certificates#update-a-verifier-root-ca-certificate)
and activate it:
```http filename:"Request"
PUT /v2/presentations/certificates/ca/{certificateId}
```
* `certificateId` : Replace with the `id` value obtained when you registered the unmanaged Verifier
root CA.
```json filename:"Request body"
{
"active": true
}
```
### Create a Verification Request [#create-a-verification-request]
Once the Verifier root CA and Verification Request Signer are activated, they can be used to sign
verification requests. MATTR VII will automatically select a valid and active Verification Request
Signer when attempting to
[create a remote verification request](/docs/verification/remote-overview#verification-requests).
If there is no valid and active Verification Request Signer, MATTR VII will return an error stating
that no valid Verification Request Signer is available for signing. Unlike the managed flow, MATTR
VII does not automatically create new Verification Request Signers in the unmanaged flow, and the
verifier is responsible for manually creating and uploading them as needed.
# Overview
URL: /docs/verification/certificates/overview
## Chain of trust [#chain-of-trust]
When verifiers request digital credentials, holders need a way to confirm the verifier’s identity
and decide whether to trust them with their information. This is accomplished using a [chain of trust](/docs/concepts/chain-of-trust),
a hierarchy of certificates that proves the verifier’s authenticity.
The following diagram depicts how MATTR implements the chain of trust model when signing
verification requests:
## Certificate requirements [#certificate-requirements]
The following lists depicts the requirements for external certificates used in MATTR VII. Some of
the requirements are common across all certificates, while others are specific to the type of
certificate (Verifier root CA, VRSC).
* When using **managed verifier certificates**, MATTR VII **automatically** ensures that all
certificates meet these requirements.
* When using **unmanaged verifier certificates**, it is the responsibility of the **verifier** to
ensure compliance.
### Common certificate requirements [#common-certificate-requirements]
* Certificate format & basic attributes:
* PEM format must contain a valid X.509 certificate.
* Version must be v3.
* `Issuer` field must be present and valid.
* Issuer Alternative Name must be present and contain a valid email address or URI.
* Serial Number:
* Must be present.
* Must contain 1-20 digits (**Best practice**: Use a positive, non-sequential value).
* Subject attributes:
* Subject field must be present:
* Country (C): must be present and be a valid
[ISO 3166-1 alpha-2 code](https://www.iso.org/glossary-for-iso-3166.html).
* Common Name (CN): must be present and non-empty.
* Public Key requirements:
* Subject Public Key must be present.
* Extensions:
* Certificate must include extensions.
* Duplicate extensions are not allowed (No more than one extension with the same `extnID`).
* Mandatory extensions:
* Subject Key Identifier: Must be present and non-empty.
* Key Usage:
* Must be present.
* Must match the intended use of the certificate (e.g. Verifier root CA, VRSC).
* Validity period:
* `NotAfter` must be after `NotBefore`.
* `NotAfter` cannot be in the past (expired certificates are invalid).
* Future dated certificates are valid (e.g. `notBefore` can be in the future).
* Must be within allowed limits for certificate type:
* VRSC: Maximum 1187 days from issuance.
* Certificate Revocation List (CRL):
* If a CRL is provided, it must be valid and signed by the verifier root CA.
* The CRL must be accessible via a valid URI.
### Verifier root CA specific requirements [#verifier-root-ca-specific-requirements]
* Must include the `keyCertSign` and `cRLSign` key usages.
* Basic constraints must be present and `CA` must be set to `TRUE`.
* Issuer Alternative Name must be present and contain a valid email address or URI.
* Signature must be self-signed and verifiable.
* Public key must use one of the supported public key algorithms and curves as defined in
ISO/IEC 18013-5:2021 B.3:
* ECDSA curves: `P-256`, `P-384`, `P-521`, `brainpoolP256r1`, `brainpoolP320r1`,
`brainpoolP384r1`, `brainpoolP512r1`
* EdDSA key types: `Ed25519`, `Ed448`
### VRSC specific requirements [#vrsc-specific-requirements]
* Must be signed by a valid Verifier root CA.
* Common name must differ from parent/root Verifier root CA.
* `Issuer` field must be present and must match the exact binary value of the Verifier root CA
certificate subject.
* Must include the `digitalSignature` key usage exclusively.
* Must include the `Subject Alternative Name` extension with a value that matches the requirement in
the CSR.
* Extended key usage must be present and include the correct OID:
* `1.0.18013.5.1.6`.
* Signature must be verifiable against the Verifier root CA.
* Authority Key Identifier must be present and match the Verifier root CA's Subject Key Identifier.
* Must not exceed parent Verifier root CA's validity period (i.e. `notBefore` and `notAfter` must be
within the Verifier root CA's validity period).
* Public key must match the CSR provided during Verification Request Signer creation.
### Example certificates [#example-certificates]
See an example of valid certificates parsed using the MATTR Labs
[X.509 certificate decoder](https://tools.mattrlabs.com/pem):
* [Verifier root CA](https://tools.mattrlabs.com/pem?cert=MIICTDCCAfKgAwIBAgIKeKcjIBGvXfS%252FsjAKBggqhkjOPQQDAjAwMQswCQYDVQQGEwJVUzEhMB8GA1UEAwwYRXhhbXBsZSBWZXJpZmllciByb290IENBMB4XDTI1MDgyNzIxMzMxNFoXDTMwMDgyNjIxMzMxNFowMDELMAkGA1UEBhMCVVMxITAfBgNVBAMMGEV4YW1wbGUgVmVyaWZpZXIgcm9vdCBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNJHM5ZE%252BfpVn7b9WwjVBiOiZq9eNXq1JkNj%252F6ZLe%252B2GkaRY%252FWE2Xbg7yx%252B%252Bh3QEdX3sGKzGO7dygQALBe%252F4qEyjgfMwgfAwHQYDVR0OBBYEFK8ogqdUH2vZlC1yNf619a8fnx8KMA4GA1UdDwEB%252FwQEAwIBBjASBgNVHRMBAf8ECDAGAQH%252FAgEAMHsGA1UdHwR0MHIwcKBuoGyGamh0dHBzOi8vbGVhcm4udmlpLmF1MDEubWF0dHIuZ2xvYmFsL3YyL3ByZXNlbnRhdGlvbnMvY2VydGlmaWNhdGVzL2Q3YzE3ODI4LThkMTgtNDYyZS1iNDk3LWNjNjI2NWM4ZmQxYi9jcmwwLgYDVR0SBCcwJYYjaHR0cHM6Ly9sZWFybi52aWkuYXUwMS5tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSAAwRQIhAOqD0DF3rohBitl5jAj6x1164uGGj6yAhF%252FeE4aJeGc%252BAiAgaUYHzobzaPEWd%252BjZOh%252FAq8WgVJ%252B8sLx9WdJDs9%252FshQ%253D%253D)
* [VRSC](https://tools.mattrlabs.com/pem?cert=MIIC4zCCAomgAwIBAgIKY6wskKU1HpGZwTAKBggqhkjOPQQDAjAwMQswCQYDVQQGEwJVUzEhMB8GA1UEAwwYRXhhbXBsZSBWZXJpZmllciByb290IENBMB4XDTI1MDgyNzIxMzMxNVoXDTI2MDIyNTIxMzMxNVowSTELMAkGA1UEBhMCVVMxOjA4BgNVBAMMMWxlYXJuLnZpaS5hdTAxLm1hdHRyLmdsb2JhbCBSZWFkZXIgQXV0aGVudGljYXRpb24wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATm3hQ84rKlvympzg%252FSEJ9jX2vP36GDLoHZLRLtaCOrOZvfS4u99ZJlDAzyFkjYVRTSdT0LmeCpu5VhbmY440X5o4IBcDCCAWwwHQYDVR0OBBYEFAWILE2UoP0%252Frk5J2RtgbXOSiH3mMA4GA1UdDwEB%252FwQEAwIHgDAuBgNVHRIEJzAlhiNodHRwczovL2xlYXJuLnZpaS5hdTAxLm1hdHRyLmdsb2JhbDBLBgNVHREERDBChiNodHRwczovL2xlYXJuLnZpaS5hdTAxLm1hdHRyLmdsb2JhbIIbbGVhcm4udmlpLmF1MDEubWF0dHIuZ2xvYmFsMHsGA1UdHwR0MHIwcKBuoGyGamh0dHBzOi8vbGVhcm4udmlpLmF1MDEubWF0dHIuZ2xvYmFsL3YyL3ByZXNlbnRhdGlvbnMvY2VydGlmaWNhdGVzL2Q3YzE3ODI4LThkMTgtNDYyZS1iNDk3LWNjNjI2NWM4ZmQxYi9jcmwwHwYDVR0jBBgwFoAUryiCp1Qfa9mULXI1%252FrX1rx%252BfHwowIAYDVR0lAQH%252FBBYwFAYJKwYBBAGD4GoCBgcogYxdBQEGMAoGCCqGSM49BAMCA0gAMEUCIQCiV7ny8MGw0S8QwkBr28hmXmsXSIbrfNimKCiDMXtAyQIgBZAy3WNqme4zBND4P7z0mFBwxC4CeJ77zaVlQrHnUZI%253D)
# MATTR GO Verify
URL: /docs/verification/go-verify/getting-started
## Overview [#overview]
**MATTR GO Verify** is a ready-to-use, white-label mobile application for in-person verification of digital credentials. It enables organisations to verify credentials confidently and securely, and can be fully customised with your own branding, colours, and typography for direct distribution to relying parties and/or end users.
The **MATTR GO Verify example app** is a publicly available reference implementation that demonstrates the capabilities of a white-labelled MATTR GO Verify app. It can be used alongside the [MATTR GO Hold example app](/docs/holding/go-hold/getting-started) to explore and test in-person credential presentation and verification workflows.
## Getting started [#getting-started]
### Download [#download]
Download the MATTR GO Verify example app to your mobile device from:
* The [App Store](https://apps.apple.com/app/mattr-go-verify/id6670461328) for iOS devices.
* [Google Play](https://play.google.com/store/apps/details?id=global.mattr.mobile.verifier) for
Android devices.
### Verify mDocs [#verify-mdocs]
1. Use a different device to download the
[MATTR GO Hold example app](/docs/holding/go-hold/getting-started#download-the-app).
2. Use the GO Hold example app to
[claim an mDoc](/docs/holding/go-hold/getting-started#claim-a-credential).
3. In the GO Hold example app select the **Share** button and then select **Share Credential**.
4. Select the *Connection QR* tab. This should display a QR code on the screen.
5. Open the GO Verify app.
6. Select the **Verify** button.
7. Scan the QR code displayed in the GO Hold example app. You may need to allow the GO Verify app to
access your camera.
8. Follow the on-screen instructions to complete the
[proximity verification](/docs/verification/in-person-overview) workflow.
### Dive deeper [#dive-deeper]
Sign up for a [MATTR VII tenant](/docs/resources/get-started), issue different credentials into your [GO Hold example app](/docs/holding/go-hold/getting-started) and then use the GO Verify example up to verify them:
* [OID4VCI Authorization Code tutorial](/docs/issuance/authorization-code/tutorial).
* [OID4VCI Pre-authorized Code tutorial](/docs/issuance/pre-authorized-code/tutorial).
# Libraries in use
URL: /docs/verification/go-verify/libraries
The following lists all notices for third party software that may be used in some way by MATTR GO
Verify and other development tools. We value the contributions by open source developers and thank
them.
# System requirements
URL: /docs/verification/go-verify/system-requirements
Description: Minimum requirements and supported devices for the MATTR GO Verify example app.
## Operating systems [#operating-systems]
The following operating system versions represent the minimum supported platforms for MATTR GO Verify.
* iOS 15 or higher
* Android 7 (API level 24) or higher
MATTR validates functionality using currently supported operating system releases provided by Apple and Google. Compatibility with manufacturer-specific Android variants may vary depending on device implementation.
For optimal security and performance, devices should run the latest available operating system updates.
## Required device permissions and configuration [#required-device-permissions-and-configuration]
Certain device permissions and user settings are required for the application to function correctly, including but not limited to the following:
* Enabling camera access: The application requires access to the device camera for in-person verification flows. If camera access is disabled, these flows will not function.
* Enabling biometrics: Some application security features rely on biometric authentication. If biometrics are not enabled on the device, these features will not be available.
* Device text size and font scaling: Extreme text size or font scaling settings may affect layout, readability, or visibility of on-screen controls. Users should configure these settings to meet their accessibility needs while still allowing the app interface to display correctly.
* Connectivity and settings: Some features require access to the public internet. Users must ensure a stable connection is available.
## Tested devices [#tested-devices]
App functionality relies on several device capabilities, including:
* Camera access for in-person verification flows
* Secure storage and hardware-backed key protection
* Biometric authentication
* Reliable network connectivity
Variations in hardware, operating system customization, and manufacturer implementations can affect these capabilities. For this reason, MATTR verifies functionality only on the devices listed below.
Devices not listed here are not verified by MATTR and functionality or performance on those devices is not guaranteed.
### iOS [#ios]
* iPhone 15 Pro
* iPhone 15
* iPhone 14 Plus
* iPhone 14
* iPhone 12
* iPhone 11 Pro
* iPhone XR
* iPhone XS
* iPhone SE
### Android [#android]
#### Samsung [#samsung]
* Samsung Galaxy S24
* Samsung Galaxy A55
* Samsung Galaxy S23 FE
* Samsung Galaxy S22
* Samsung Galaxy S20 FE 5G
* Samsung Galaxy Note20 5G
#### Google [#google]
* Google Pixel 7 Pro
* Google Pixel 7
* Google Pixel 6 Pro
MATTR may update the list of verified devices and supported operating system versions as new devices and platform updates are released. Customers should ensure their environments remain aligned with the current requirements.
# Handling verification results
URL: /docs/verification/in-person-guides/handling-verification-results
Description: Learn how to interpret mDoc verification results returned by the mDocs Verifier SDK in proximity presentation flows.
When a holder responds to an in-person (proximity) verification request, the mDocs Verifier SDK returns structured data containing the verification outcome. This guide explains the response structure and how to interpret it.
## What gets verified [#what-gets-verified]
Before working through the response structure, it is worth being clear about what is actually
being verified. The credential itself, the full mDoc as issued and stored in the holder's
wallet, never leaves the wallet and is never shared with a verifier. What the holder shares is
a *credential presentation*: a subset of the credential's data, accompanied by the issuer's
signature over the released items and a device authentication that proves the holder's device
authorized this specific presentation.
This model is grounded in [ISO/IEC 18013-5](https://www.iso.org/standard/69084.html), which
defines the mDL/mDoc data model and the proximity presentation flow used here. The standard
treats what the holder shares as a presentation derived from the credential rather than the
credential itself, and the presentation carries the same cryptographic proof and integrity
guarantees as the credential it is drawn from.
When you verify a response, you are verifying the credential presentation. The cryptographic
trust checks (issuer signature, issuer trust, validity, revocation, device key binding) apply
to that presented slice, not to the credential as a whole on the holder's device. The MATTR VII
`verificationResult.verified` flag reflects whether this presented credential passed those
checks.
The rest of this guide uses "credential" as shorthand for the presented credential when the
context makes clear we are talking about what was returned in the response.
## How results are delivered [#how-results-are-delivered]
In a proximity presentation flow, the holder's device communicates directly with your verifier application. After the holder responds to the presentation request, the SDK returns a `MobileCredentialResponse` object.
Your application receives the result inline and is responsible for parsing and acting on it according to your business logic.
## Understanding the response [#understanding-the-response]
A `MobileCredentialResponse` contains two optional fields:
* **`credentials`**: An array of `MobileCredentialPresentation` objects representing credentials that were successfully returned by the holder.
* **`credentialErrors`**: An array of `CredentialError` objects representing credentials that couldn't be returned.
When a holder responds to a verification request, several outcomes are possible, often in
combination. Each one is described along two independent axes: whether the requested credential
itself verified, and whether the requested claims within it were provided.
1. **Requested credential not presented**: The holder doesn't have the requested credential, so there is nothing to verify. The credential appears under `credentialErrors` with an error code of `notReturned`, and no `verificationResult` is produced.
2. **Presented credential not verified**: A credential was provided but the credential-level trust checks failed. The `credentials` array contains the credential with `verified: false` and a `reason.type` explaining why. Even if claims were returned, they should not be relied on while the credential itself did not verify.
3. **Presented credential verified, all claims provided**: The credential-level checks pass (`verified: true` with no `reason`) and every requested claim is returned (no `claimErrors`). Both layers are clean.
4. **Presented credential verified, some claims missing**: The credential-level checks pass (`verified: true`), but one or more requested claims were not returned and appear under `claimErrors`. The credential is trustworthy; the data payload is incomplete. How to handle the missing data is up to the relying party, depending on its use case. Options include failing the flow, falling back to collecting the required data through another channel, or proceeding if the missing claim is not essential.
## Detailed result structure [#detailed-result-structure]
### Credential-level information [#credential-level-information]
Each `MobileCredentialPresentation` in the `credentials` array contains:
* **`docType`**: The credential type (e.g., `org.iso.18013.5.1.mDL` for a mobile driver's license).
* **`claims`**: Verified claims organized by namespace.
* **`claimErrors`**: Errors for individual claims that couldn't be verified or were not returned.
* **`validityInfo`**: Credential validity period timestamps (`signed`, `validFrom`, `validUntil`, `expectedUpdate`).
* **`verificationResult`**: Verification status containing:
* **`verified`**: Boolean indicating if verification succeeded. This is a high-level result; individual claim errors may still exist.
* **`reason`** (optional): Object explaining verification failures when `verified` is `false`. Contains a `type` value from the following:
* `DeviceKeyInvalid`: Device key is not valid. This can occur if the credential was not properly bound to the device or if the device's secure element is compromised.
* `InvalidSignerCertificate`: Invalid signer certificate. This can occur if the credential's signing certificate is not valid or has been tampered with.
* `IssuerNotTrusted`: Credential was issued by a certificate that is not trusted by the verifier. This can occur if the issuer's certificate is not included in the verifier's trusted issuer list.
* `MobileCredentialExpired`: Credential expired. This can occur if the current date is after the credential's `validUntil` date.
* `MobileCredentialInvalid`: Credential is not valid. This can occur for various reasons such as failing signature verification, containing invalid data, or not conforming to expected formats.
* `MobileCredentialNotYetValid`: Credential not yet valid. This can occur if the current date is before the credential's `validFrom` date.
* `StatusRevoked`: Credential has been revoked. This can occur if the issuer has revoked the credential after it was issued, which is typically checked through a revocation mechanism provided by the issuer.
* `StatusUnknown`: Credential status could not be determined. This can occur if the verifier is unable to check the revocation status of the credential due to network issues or if the issuer does not provide a revocation mechanism.
* `TrustedIssuerCertificateExpired`: Trusted issuer certificate expired. This can occur if the certificate of the trusted issuer has passed its expiration date, which may affect the trustworthiness of credentials issued by that issuer.
* `TrustedIssuerCertificateNotYetValid`: Trusted issuer certificate not yet valid. This can occur if the certificate of the trusted issuer is not yet valid (i.e., the current date is before the certificate's `validFrom` date), which may affect the trustworthiness of credentials issued by that issuer.
* `UnsupportedCurve`: Credential object contains an unsupported curve. This can occur if the credential uses cryptographic curves that are not supported by the verifier's cryptographic library, which may prevent successful verification of the credential's signatures.
* **`issuerInfo`** (optional): Issuer details including `commonName` and `trustedIssuerId`.
* **`branding`** (optional): Visual information for displaying the credential (name, description, colors, logos). You can use this to create a rich user interface when showing credential details in your application.
The examples on this page show the serialized response, where the failure object is the `reason`
field. In the native iOS (v6.0.0+) and Android (v7.0.0+) Verifier SDKs, this is accessed in code via
the `failureType` property (it still serializes as `reason`). In the React Native SDK it is accessed
as `reason`.
### Claim-level information [#claim-level-information]
Claims are organized by namespace within the `claims` object. For example, for an mDL, claims appear under the `org.iso.18013.5.1` namespace:
```json title="Example claims structure for a verified mDL credential"
"claims": {
"org.iso.18013.5.1": {
"family_name": { "value": "Smith" },
"given_name": { "value": "Jane" },
"birth_date": { "value": "1990-05-15" },
"address": { "value": "123 Main Street, Springfield" }
}
}
```
If specific claims couldn't be verified or weren't provided, they appear in the `claimErrors` object with the same namespace structure and an error code of `notReturned`:
```json title="Example claim errors for a credential with a missing claim"
"claimErrors": {
"org.iso.18013.5.1": {
"portrait": "notReturned"
}
}
```
### Credential errors [#credential-errors]
When a requested credential wasn't provided by the holder, it appears in the `credentialErrors` array with a `docType` and an `errorCode` of `notReturned`:
```json title="Example credential errors for a missing credential"
"credentialErrors": [
{
"docType": "org.iso.18013.5.1.mDL",
"errorCode": "notReturned"
}
]
```
## Understanding the `verified` flag [#understanding-the-verified-flag]
With the structure in mind, it is worth being explicit about what
`verificationResult.verified` does and does not tell you, because it is the single most important
field for deciding whether to trust a presentation.
`verified` is the pass/fail flag for the presented credential. It applies to the slice of
credential data the holder released in this session, together with its associated issuer
signature and device authentication, not to the credential as a whole as stored on the holder's
device. When `verified: true`, what the holder presented has passed MATTR VII's cryptographic
and trust checks: the issuer signature on the released items is intact, the credential was
issued by a trusted issuer, it is not expired or revoked, and the device key binding holds. In
plain terms, the presented mDoc is genuine, has not been tampered with, and is currently valid.
What `verified: true` does not mean is that every claim you requested came back. `verified` is a
high-level result on the credential as a whole, and individual claim errors may still exist. A
credential can return `verified: true` while a specific requested claim (for example, `portrait`)
is missing and surfaces under `claimErrors` with an error code of `notReturned`. The credential
is trustworthy; the data payload may still be incomplete.
The mirror image is `verified: false`. When the credential layer fails, MATTR VII surfaces a
`reason.type` explaining why (for example, expired, revoked, untrusted issuer, invalid signer
certificate, or broken device key binding). The `reason` field is typed as optional, so
defensive code should handle the case where it is absent. See the
[credential-level information](#credential-level-information) section above for the full list of
`reason.type` values.
### Two layers of checks [#two-layers-of-checks]
A relying party's business logic needs to check two distinct things:
1. **Is the credential real and valid?** Look at `verificationResult.verified` on each credential
in the `credentials` array. This is the objective trust check, and it should not be overridden
by business logic.
2. **Did we get all the data we asked for?** Look at `claimErrors` on each credential, and at
`credentialErrors` on the top-level result. Whether a missing claim or credential is
acceptable is a business-logic decision specific to your use case.
In other words, `verified` is the objective measure of what can or cannot be accepted at all.
Everything else, including `claimErrors`, `credentialErrors`, and the specific claim values
returned, feeds your business logic about whether to accept the presentation for your particular
use case.
Treating `verified: true` as a green light to proceed without inspecting `claimErrors` is a
common integration mistake. The credential may be cryptographically valid while still missing a
field your use case requires. For example, an age-restricted purchase flow needs `birth_date`,
but if only `family_name` came back the relying party cannot make an age decision even though
the credential itself returned `verified: true`. Conversely, bypassing a `verified: false`
result with business logic (for example, accepting an expired or revoked credential because the
claims look correct) defeats the trust model and should not be done.
`verified` is MATTR VII's API shape. The underlying trust evaluation for mDL and mDoc
credentials follows ISO/IEC 18013-5, but the `verified` boolean itself is not a standards-defined
field.
## Complete examples [#complete-examples]
### Requested credential not presented [#requested-credential-not-presented]
Neither layer of the check produces a result here. The holder did not provide a matching mDL, so
the credential layer has nothing to verify (no `verificationResult` is returned) and the claims
layer is not applicable. The missing credential is reported under `credentialErrors`, and the
relying party's business logic must decide how to handle it.
```json title="Example response for a requested credential that was not presented"
{
"credentialErrors": [
{
"docType": "org.iso.18013.5.1.mDL",
"errorCode": "notReturned"
}
]
}
```
### Presented credential not verified [#presented-credential-not-verified]
The credential layer fails. The holder provided an mDL but the credential-level trust checks
failed (in this example, because the credential has expired): `verified: false`, with
`reason.type` identifying why. Even if claims were returned, they should not be relied on while
the credential itself did not verify. The credential should not be accepted regardless of
business logic.
```json title="Example response for a presented credential that did not verify (expired mDL)"
{
"credentials": [
{
"docType": "org.iso.18013.5.1.mDL",
"verificationResult": {
"verified": false,
"reason": {
"type": "MobileCredentialExpired",
"message": "Credential has expired"
}
},
"validityInfo": {
"signed": "2024-01-15T10:00:00Z",
"validFrom": "2024-01-15T10:00:00Z",
"validUntil": "2025-01-15T10:00:00Z",
"expectedUpdate": "2026-01-15T10:00:00Z"
}
}
]
}
```
### Presented credential verified, all claims provided [#presented-credential-verified-all-claims-provided]
Both layers pass. The credential layer returns `verified: true` with no `reason`, confirming the
mDL is genuine, current, and from a trusted issuer. The claims layer returns every requested
claim with no `claimErrors`, so the relying party has the complete data payload it asked for.
```json title="Example response for a presented credential that verified with all requested claims returned"
{
"credentials": [
{
"docType": "org.iso.18013.5.1.mDL",
"claims": {
"org.iso.18013.5.1": {
"family_name": { "value": "Smith" },
"given_name": { "value": "Jane" },
"birth_date": { "value": "1990-05-15" },
"address": { "value": "123 Main Street, Springfield" }
}
},
"validityInfo": {
"signed": "2024-01-15T10:00:00Z",
"validFrom": "2024-01-15T10:00:00Z",
"validUntil": "2027-01-15T10:00:00Z",
"expectedUpdate": "2028-01-15T10:00:00Z"
},
"verificationResult": {
"verified": true
},
"issuerInfo": {
"commonName": "State Department of Motor Vehicles",
"trustedIssuerId": "d4a6e9f2-3b1c-4d8e-a5f7-9c2b0e8d1a3f"
},
"branding": {
"name": "Driver License",
"description": "State-issued driver license",
"backgroundColor": "#1E3A8A",
"issuerLogo": {
"format": "svg",
"data": "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg=="
}
}
}
]
}
```
### Presented credential verified, some claims missing [#presented-credential-verified-some-claims-missing]
The credential layer passes but the claims layer is incomplete. The mDL itself returns
`verified: true` and is trustworthy, while one requested claim (`portrait`) was not released and
appears under `claimErrors` as `notReturned`. What is missing here is data, not trust, and the
relying party's business logic must decide whether the missing claim is acceptable for its use
case.
```json title="Example response for a presented credential that verified with a missing claim"
{
"credentials": [
{
"docType": "org.iso.18013.5.1.mDL",
"claims": {
"org.iso.18013.5.1": {
"family_name": { "value": "Smith" },
"given_name": { "value": "Jane" },
"birth_date": { "value": "1990-05-15" }
}
},
"claimErrors": {
"org.iso.18013.5.1": {
"portrait": "notReturned"
}
},
"validityInfo": {
"signed": "2024-01-15T10:00:00Z",
"validFrom": "2024-01-15T10:00:00Z",
"validUntil": "2027-01-15T10:00:00Z",
"expectedUpdate": "2028-01-15T10:00:00Z"
},
"verificationResult": {
"verified": true
},
"issuerInfo": {
"commonName": "State Department of Motor Vehicles",
"trustedIssuerId": "d4a6e9f2-3b1c-4d8e-a5f7-9c2b0e8d1a3f"
}
}
]
}
```
## Recommended handling flow [#recommended-handling-flow]
When processing a `MobileCredentialResponse`, follow these steps:
1. **Check for credential errors**: Inspect `credentialErrors` to determine if any requested credentials were not returned by the holder. Handle these based on whether the credential is required for your use case.
2. **Iterate through credentials**: For each `MobileCredentialPresentation` in the `credentials` array:
* Check `verificationResult.verified`. If `false`, inspect `verificationResult.reason` to understand why.
* If `verified` is `true`, proceed to extract claim values from the `claims` object.
* Check `claimErrors` for any claims that were requested but not returned. Decide whether to proceed based on which claims are missing.
3. **Apply business logic**: Use the verified claims, issuer information, and branding data to make authorization decisions and present results in your application.
## Next steps [#next-steps]
* Review the mDocs Verifier SDK API reference for [iOS](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/) or [Android](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/) for complete type definitions
* See the [in-person verification quickstart](/docs/verification/in-person-quickstart) for building a proximity verifier
* Learn how to configure [revocation status checks](/docs/verification/in-person-guides/revocation-status-check) for in-person verification flows
# How to implement mDocs revocation status checks in your verifier application
URL: /docs/verification/in-person-guides/revocation-status-check
## Overview [#overview]
This guide demonstrates how to implement revocation status checks for mDocs in your verifier applications. By implementing status checks, your verifier can confirm whether credentials have been revoked or remain valid before accepting them.
MATTR's implementation of mDocs revocation is based on Draft 14 of the IETF [Token Status List](https://drafts.oauth.net/draft-ietf-oauth-status-list/draft-ietf-oauth-status-list.html) specification. A revocable mDoc includes a reference to a status list which is managed by the issuer. The list contains the revocation status of multiple credentials, and each credential references the index of its status within a specific status list.
Status lists are automatically created and managed by the issuer's MATTR VII tenant when issuing revocable mDocs. They are publicly available and mainly consumed by verifier applications to check the status of presented mDocs.
For detailed information about how mDocs revocation works, including status list structure, tokens, and signing, see the [Revocation documentation](/docs/issuance/revocation/overview#mdocs).
## Prerequisites [#prerequisites]
This guide builds on the [In-person Verification tutorial](/docs/verification/in-person-tutorial). It is recommended to complete that tutorial first, then return here to learn how to implement status checks.
## Understanding revocation status [#understanding-revocation-status]
### Status values [#status-values]
When verifying a presented mDoc, the revocation status can be one of the following:
* **Valid**: The mDoc is valid.
* **Invalid**: The mDoc is permanently revoked.
* **Suspended**: ⚠️ **Deprecated** ⚠️ The mDoc is temporarily revoked. Only applicable to legacy credentials issued before the adoption of Draft 14 of the Token Status List specification.
* **Unknown**: The status cannot be determined, typically because the status list is unavailable or has expired. The verifier application must decide whether to accept or reject credentials with unknown status based on their security requirements.
### How status information is stored [#how-status-information-is-stored]
When a revocable mDoc is issued, it includes a `status` object in its MSO payload that references a status list:
```json title="mDoc status reference"
"status": {
"status_list": {
"uri": "https://learn.vii.au01.mattr.global/v2/credentials/mobile/status-lists/f331c9be-f526-4577-bbac-ae93d6228f7a/token",
"idx": 0
}
}
```
* `status_list`: References the status list that holds the status information for this mDoc.
* `uri`: The publicly available endpoint where the status list token can be retrieved.
* `idx`: The index of this mDoc's status within the referenced status list.
When a verifier retrieves a status list, the issuer cannot tell what specific mDoc they are checking the status for. This preserves holder privacy - the issuer does not know how often or to whom an mDoc is being presented.
## Status list caching and updates [#status-list-caching-and-updates]
When retrieving a status list, the response is a signed status list token (a [CBOR Web Token](https://datatracker.ietf.org/doc/html/rfc8392)) that includes:
* `iat`: Timestamp when the status list token was signed.
* `exp`: Expiry timestamp.
* `ttl`: Recommended duration in seconds before fetching a new token.
* `status_list`: The compressed status list containing the status of all mDocs included in this list.
Retrieving a status list for every credential operation would create performance issues and make offline presentation impossible. To address this, MATTR uses a caching mechanism based on the `ttl` and `exp` values:
* After retrieving a status list, the application should not fetch it again until the `ttl` has passed, as there are unlikely to be any changes. This optimizes performance and reduces unnecessary network requests.
* If the application fails to retrieve an updated status list **after the TTL** (for example, because the device is offline), it can continue using the cached status until the status list token expiry date (`exp`).
* If the **expiry date** passes without a successful update, the credential status can no longer be trusted and it is changed to `Unknown`. It is then up to the application to decide how to handle credentials with `Unknown` status.
### How the SDK manages cache updates [#how-the-sdk-manages-cache-updates]
The Verifier SDK automatically calculates a `nextUpdateDate` for each cached status list token. This date is determined as the **earlier** of:
* Token retrieval time + `ttl`
* Token expiry time (`exp`)
Based on this calculation:
* **Before `nextUpdateDate`**: The SDK continues to use the cached token without attempting to fetch a new one.
* **After `nextUpdateDate`**: The SDK attempts to retrieve a new status list token when status verification is performed.
### Offline behavior [#offline-behavior]
When the verifier device is offline:
* If the current time is **after `ttl` but before `exp`**: The SDK cannot retrieve a status list (as the device is offline), the SDK will continue to use the cached status.
* If the current time is **after `exp`**: The cached status list is no longer valid, and the status will be returned as `Unknown`.
This behavior ensures that verifiers don't rely on stale status information beyond the issuer's intended timeframe.
## Implementing status checks [#implementing-status-checks]
By default, the Verifier SDK checks the revocation status of every credential as part of its verification workflow. This ensures that revoked credentials are not accepted.
When sending a proximity presentation request, you can control whether status checks should be performed using the `checkStatus` parameter:
```swift title="Proximity presentation with status check"
// Request mDoc with automatic status check (default behavior)
let response = try await verifier.sendProximityPresentationRequest(
request: request,
checkStatus: true
)
// Request mDoc without status check
let response = try await verifier.sendProximityPresentationRequest(
request: request,
checkStatus: false
)
```
```kotlin title="Proximity presentation with status check"
// Request mDoc with automatic status check (default behavior)
val response = MobileCredentialVerifier.sendProximityPresentationRequest(
request,
checkStatus = true
)
// Request mDoc without status check
val response = MobileCredentialVerifier.sendProximityPresentationRequest(
request,
checkStatus = false
)
```
```typescript title="Proximity presentation with status check"
// Request mDoc with automatic status check (default behavior)
const response = await verifier.sendProximityPresentationRequest({
request,
checkStatus: true,
});
// Request mDoc without status check (for faster verification)
const response = await verifier.sendProximityPresentationRequest({
request,
checkStatus: false,
});
```
### Status check flow [#status-check-flow]
1. When a proximity presentation request is sent, the SDK first checks the `checkStatus` parameter.
2. If `checkStatus` is `false`, the SDK skips status checking entirely and returns the credential without status information.
3. If `checkStatus` is `true` (the default), the SDK checks the credential's revocation status:
* The SDK checks if the current time is after the cached status list's `nextUpdateDate`.
* If after `nextUpdateDate`, the SDK attempts to retrieve an updated status list.
* If the retrieval succeeds, the status is updated based on the new status list.
* If the retrieval fails (e.g., device is offline), the SDK checks if the cached status list has expired.
* There is no way to force retrieval of an updated status list if the `nextUpdateDate` hasn't been reached. The SDK will always use the cached status list until the TTL expires.
4. When using a cached status list (either because `nextUpdateDate` hasn't passed or retrieval failed):
* If the cached status list hasn't expired, the status is returned from the cache.
* If the cached status list has expired, the status becomes `Unknown`.
Setting `checkStatus: false` will bypass status check entirely. Use this only when you need faster verification and have an alternative method to check credential status, or when operating in environments where status checking is not required.
## Proactive cache management [#proactive-cache-management]
For better control over status list freshness and offline use cases, the Verifier SDK provides methods to manage the cache proactively:
### Updating status lists [#updating-status-lists]
Use the SDK's revocation status list refresh method to force a refresh of all relevant status list tokens. This fetches the latest status list tokens from all trusted issuers.
From iOS Verifier SDK v6.0.0 and Android Verifier SDK v7.0.0, `updateTrustedIssuerStatusLists` was renamed to `refreshRevocationStatusLists` (and `getTrustedIssuerStatusListsCacheInfo` to `getRevocationStatusListsCacheInfo`). The React Native SDK retains the original method names. See the [iOS](/docs/verification/sdks/ios-6.0.0-migration-guide) and [Android](/docs/verification/sdks/android-7.0.0-migration-guide) migration guides for details.
From v6.0.0, `refreshRevocationStatusLists` returns a `RevocationStatusListsRefreshResult` `@frozen` enum with `success` and `failure` cases:
```swift title="Update status lists"
do {
switch try await verifier.refreshRevocationStatusLists() {
case .success(let nextUpdate):
// All status lists refreshed; schedule the next refresh before nextUpdate
print("All status lists updated successfully")
case .failure(let nextUpdate, let failedLists):
// failedLists holds the URIs that failed to refresh, keyed by trusted issuer certificate ID
print("Some status lists failed to update: \(failedLists)")
}
} catch {
print("Failed to update status lists: \(error.localizedDescription)")
}
```
On Android, `refreshRevocationStatusLists` returns a `RevocationStatusListsRefreshResult` sealed interface with `Success` and `Failure` variants:
```kotlin title="Update status lists"
when (val result = MobileCredentialVerifier.refreshRevocationStatusLists()) {
is RevocationStatusListsRefreshResult.Success -> {
// All status lists refreshed successfully
}
is RevocationStatusListsRefreshResult.Failure -> {
// result.failedLists is available here
}
}
```
```typescript title="Update status lists"
try {
await verifier.updateTrustedIssuerStatusLists();
console.log("All status lists updated successfully");
} catch (error) {
console.error("Failed to update status lists:", error);
}
```
**When to use this method:**
* **Before the `nextUpdateDate`**: Call this regularly (e.g., when the app starts or resumes) to ensure up-to-date verification without waiting for the `nextUpdateDate` to pass.
* **During idle periods**: Update status lists when the verifier app is not actively verifying credentials.
* **After connectivity is restored**: If the device was offline and is now back online, refresh the status lists.
### Inspecting cache metadata [#inspecting-cache-metadata]
Use the SDK's cache info method to inspect the `nextUpdate` date for the status lists. On iOS (v6.0.0+) and Android (v7.0.0+) this is `getRevocationStatusListsCacheInfo()`; the React Native SDK retains `getTrustedIssuerStatusListsCacheInfo()`.
From v6.0.0, `getRevocationStatusListsCacheInfo()` is synchronous (it throws but is not `async`) and returns a single `RevocationStatusListsCacheInfo` with an optional `nextUpdate` date:
```swift title="Inspect cache info"
do {
let cacheInfo = try verifier.getRevocationStatusListsCacheInfo()
// nextUpdate is nil if the cache is empty or all cached status lists have expired
if let nextUpdate = cacheInfo.nextUpdate, Date() > nextUpdate {
print("Status lists should be updated")
}
} catch {
print("Failed to get cache info: \(error.localizedDescription)")
}
```
```kotlin title="Inspect cache info"
val cacheInfo = MobileCredentialVerifier.getRevocationStatusListsCacheInfo()
val nextUpdate = cacheInfo.nextUpdate
if (nextUpdate != null && nextUpdate < Clock.System.now()) {
MobileCredentialVerifier.refreshRevocationStatusLists()
}
```
```typescript title="Inspect cache info"
try {
const cacheInfo = await verifier.getTrustedIssuerStatusListsCacheInfo();
for (const info of cacheInfo) {
console.log("Issuer:", info.issuer);
console.log("Next update date:", info.nextUpdateDate);
console.log("Expiry date:", info.expiryDate);
// Check if update is needed
if (Date.now() > info.nextUpdateDate) {
console.log("Status list should be updated");
}
}
} catch (error) {
console.error("Failed to get cache info:", error);
}
```
**When to use this method:**
* **Monitoring cache health**: Check when the next update is due.
* **Conditional updates**: Decide whether to trigger a status list refresh (`refreshRevocationStatusLists()` on iOS/Android, `updateTrustedIssuerStatusLists()` on React Native) based on the next update date (`nextUpdate` on iOS/Android, `nextUpdateDate` on React Native).
## Checking credential status [#checking-credential-status]
After receiving a presentation response, you can check the status of each credential to determine whether it should be accepted:
```swift title="Checking credential status"
let response = try await verifier.sendProximityPresentationRequest(
request: request,
checkStatus: true // checkStatus
)
for credential in response.credentials {
// Check the overall verification result
if credential.verificationResult.verified {
print("Credential verification passed")
// Check the credential status
if let status = credential.status {
switch status {
case .valid:
print("Status: Valid - Accept credential")
// Accept the credential
case .invalid:
print("Status: Invalid - Reject credential (permanently revoked)")
// Reject the credential
// Deprecated: .suspended only applies to legacy credentials issued before Draft 14 of the Token Status List specification
case .suspended:
print("Status: Suspended - Reject credential (temporarily revoked)")
// Reject the credential
case .unknown:
print("Status: Unknown - Cannot determine status")
// Handle according to your security requirements
@unknown default:
print("Status: Unexpected value")
}
} else {
print("No status information available (credential may not be revocable)")
// Handle non-revocable credentials
}
} else {
print("Credential verification failed: \(credential.verificationResult.failureType?.message ?? "Unknown reason")")
// Reject the credential
}
}
```
```kotlin title="Checking credential status"
val response = verifier.sendProximityPresentationRequest(
request = request,
checkStatus = true
)
response.credentials.forEach { credential ->
val verificationResult = credential.verificationResult
if (verificationResult?.verified == true) {
Log.d("Tag", "Credential is valid.")
} else {
when (verificationResult?.failureType) {
MobileCredentialVerificationFailureType.StatusRevoked -> {
Log.d("Tag", "Credential has been revoked.")
// Display warning, prevent presentation
}
// Deprecated: StatusSuspended only applies to legacy credentials issued before Draft 14 of the Token Status List specification
MobileCredentialVerificationFailureType.StatusSuspended -> {
Log.d("Tag", "Credential has been suspended.")
// Display warning, prevent presentation
}
MobileCredentialVerificationFailureType.StatusUnknown -> {
Log.d("Tag", "Credential status is unknown.")
// Display warning, handle according to your security requirements
}
else -> {
// Handle other failure types
}
}
}
}
```
```typescript title="Checking credential status"
const response = await verifier.sendProximityPresentationRequest({
request,
checkStatus: true,
});
for (const credential of response.credentials) {
// Check the overall verification result
if (credential.verificationResult.verified) {
console.log("Credential verification passed");
// Check the credential status
if (credential.status) {
switch (credential.status) {
case CredentialStatus.Valid:
console.log("Status: Valid - Accept credential");
// Accept the credential
break;
case CredentialStatus.Invalid:
console.log("Status: Invalid - Reject credential (permanently revoked)");
// Reject the credential
break;
// Deprecated: CredentialStatus.Suspended only applies to legacy credentials issued before Draft 14 of the Token Status List specification
case CredentialStatus.Suspended:
console.log("Status: Suspended - Reject credential (temporarily revoked)");
// Reject the credential
break;
case CredentialStatus.Unknown:
console.log("Status: Unknown - Cannot determine status");
// Handle according to your security requirements
break;
}
} else {
console.log("No status information available (credential may not be revocable)");
// Handle non-revocable credentials
}
} else {
console.log(`Credential verification failed: ${credential.verificationResult.reason?.message}`);
// Reject the credential
}
}
```
## Handling offline scenarios [#handling-offline-scenarios]
* **Cache reliance**: When offline, the SDK relies on cached status lists.
* **Cached status after TTL**: If the device is offline and `ttl` has passed (but expiry hasn't), the cached status will be used.
* **Unknown status after expiry**: If the device is offline and `exp` has passed, the status will always be returned as `Unknown`.
* **Unknown status handling**: Define your application's policy for handling credentials with `Unknown` status. Options include:
* Rejecting credentials (most secure) - Recommended for high-security scenarios.
* Accepting with warnings - May be appropriate for lower-risk scenarios.
* **User communication**: Clearly inform verifiers when status checks fail and provide guidance on how to proceed.
# CWT credentials in-person verification journey pattern
URL: /docs/verification/in-person-journey-patterns/cwt
This journey pattern is used to verify a CWT credential presented in-person.
## Overview [#overview]
* **Issuance channel**: In-person, Supervised
* **Device/s**: Cross-device
* **Formats**: CWT
* **Information assurance level**: High
* **Identity assurance level**: Low (can be high if holder presents an additional identity
document)
## Journey flow [#journey-flow]
## Architecture [#architecture]
### Credential presentation [#credential-presentation]
The holder uses their digital wallet (1) to select the appropriate credential to be presented to the
verifier. Once selected, the wallet renders the credential as a QR code.
Alternatively, the holder can also print this QR code and present a paper-based credential.
### Credential verification [#credential-verification]
The verifier scans the QR code presented by the holder's wallet using a verification app (2) that
can be an application built with the MATTR Pi Verifier SDK or a MATTR GO Verify branded application.
The credential’s validity is checked for the following:
* The digital signature is valid, indicating the content of the credential hasn’t been tampered
with.
* The credential hasn’t been revoked by the issuer.
* The credential is currently valid and hasn’t expired yet (based on valid from and valid until
values included in the credential).
* It has been issued by an issuer that is trusted to issue this type of credential according to
the Digital Trust Service (3).
The only reliance the verification has on the issuer is calling an online revocation list (if the
credential has revocation properties), which the credential issuer may host to check the status of
the credential being presented.
No information relating to the verifier or the context in which the holder is utilizing the
credential is shared or available to the issuer.
Verification can also be achieved by extracting the credential payload and passing it through to a
MATTR VII verifier tenant.
# In-person IDV integration pattern
URL: /docs/verification/in-person-journey-patterns/idv-integration
This journey pattern is used to verify a credential presented via a
[proximity verification workflow](/docs/verification/in-person-overview) as part of an in-person
high-assurance interaction.
## Overview [#overview]
* **Issuance channel**: In-person, Supervised
* **Device/s**: Cross-device
* **Formats**: mDocs
* **Information assurance level**: Very High
* **Identity assurance level**: High (exact identity assurance levels depends on specific IDV blocks
implemented in the workflow).
## Journey flow [#journey-flow]
## Architecture [#architecture]
### Establishing the connection [#establishing-the-connection]
The user uses their digital wallet (1) to choose an mDL to present, which renders a QR code on the
Holder’s device screen.
The verifier application (2) can be an application built with the MATTR Pi Verifier SDK or a MATTR
GO Verify branded application. It is used to scan the presented QR code and set up a secure
Bluetooth Low Energy (BLE) connection with the user’s device.
### Sending a presentation request [#sending-a-presentation-request]
Once the Bluetooth connection is established, the verifier requests specific information by sending
a presentation request. This request contains all the information needed for the digital wallet or
holding device to understand:
* What information is being requested.
* Who is asking for it.
* Where the wallet should respond to.
* Required assurance levels.
### Reviewing the presentation request [#reviewing-the-presentation-request]
The digital wallet (1) will locate any stored credentials that match the information specified in
the request and present them to the user, indicating what information is requested by the verifier.
### Sharing the information [#sharing-the-information]
The user consents to sharing the information, which is then bundled by the wallet as a verifiable
presentation, cryptographically signed, and sent to the verifier application as a presentation
response.
### Verifying the information [#verifying-the-information]
The verifier application (2) interprets and verifies the credentials:
* The digital signature is valid, indicating the content of the credential hasn't been tampered
with.
* It hasn’t been revoked or suspended by the issuer.
* The credential is currently valid and hasn't expired yet (based on valid from and valid until
values included in the credential).
* It’s been issued by an issuer that is trusted to issue this type of credential according to
information retrieved from the Digital Trust Service (3).
Verification results are then displayed by the verifier application, enabling the physical
interaction to continue accordingly.
# mDocs in-person verification journey pattern
URL: /docs/verification/in-person-journey-patterns/mdocs
This journey pattern is used to verify an mDoc in-person via a
[proximity verification workflow](/docs/verification/in-person-overview), as per
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
## Overview [#overview]
* **Issuance channel**: In-person, supervised
* **Device/s**: Cross-device
* **Formats**: mDocs
* **Information assurance level**: High
* **Identity assurance level**: High
## Journey flow [#journey-flow]
## Architecture [#architecture]
### Establishing the connection [#establishing-the-connection]
The user opens their digital wallet app on their mobile device and selects a credential they wish to present. They then establish a secure connection with the verifier in one of the following ways:
The wallet app displays a QR code on the screen. The verifier application, which may be operated by a person or run on a self-service kiosk, scans this QR code using its device. Scanning the QR code initiates a secure Bluetooth Low Energy (BLE) connection between the two devices.
On Android devices, the BLE connection can also be initiated via NFC tap.
### Sending a presentation request [#sending-a-presentation-request]
Once the secure connection is established, the verifier application sends a presentation request to the wallet app. This request includes:
* The specific information being requested (e.g. claims or attributes from a credential).
* The identity of the verifier making the request.
* The required assurance level for the data.
* Instructions on how and where the response should be returned.
### Reviewing the presentation request [#reviewing-the-presentation-request]
The wallet app processes the presentation request and identifies any matching credentials stored on the device. It presents this information to the user, showing:
* Which credential(s) can satisfy the request.
* Exactly what data will be disclosed if the user proceeds.
* Whether selective disclosure is available for that credential, allowing the user to share only the requested claims.
* Whether the verifier is trusted, using information from a Digital Trust Service configured for the current trust network.
### Sharing the information [#sharing-the-information]
If the user consents, the wallet app:
* Creates a verifiable presentation using the selected credential and requested data.
* Cryptographically signs the presentation.
* Sends the signed presentation back to the verifier app over the BLE connection.
### Verifying the information [#verifying-the-information]
The verifier application receives the presentation and performs a series of verification checks, including:
* Validating the digital signature to confirm the data has not been tampered with.
* Checking that the credential has not been revoked or suspended, using a revocation list (if applicable).
* Verifying that the credential is currently valid, based on its “valid from” and “valid until” dates.
* Ensuring the credential was issued by a trusted issuer, based on information retrieved from a Digital Trust Service.
The issuer of the credential is not informed that the presentation has occurred. No data about the verifier, the context of use, or the interaction itself is shared with the issuer. The only interaction with the issuer is a potential call to an online revocation endpoint, if revocation checking is required.
# mDocs remote mobile verification journey pattern
URL: /docs/verification/remote-mobile-verifiers/journey-pattern
This journey pattern is used to verify an mDoc remotely by presenting it to an app installed on the same mobile device as the digital wallet, as per
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html).
## Overview [#overview]
* **Issuance channel**: Remote, unsupervised
* **Device/s**: Same-device
* **Formats**: mDocs
* **Information assurance level**: High
* **Identity assurance level**: High
## Journey flow [#journey-flow]
## Architecture [#architecture]
### Interacting with the mobile application [#interacting-with-the-mobile-application]
The user accesses a mobile app that embeds the MATTR Pi Verifier Mobile SDK. The app initiates and handles the entire verification flow on the same device.
### Requesting a credential for verification [#requesting-a-credential-for-verification]
The Verifier Mobile SDK sends a verification request to a configured MATTR VII verifier tenant, defining:
* The credentials and claims required
* The supported interaction mode (same-device)
The MATTR VII verifier tenant is configured with:
* Which apps or domains can issue verification requests
* The workflows it supports (same-device and/or cross-device)
* The protocols it supports (e.g. OID4VP, Apple’s Verify with Wallet API)
* Which wallet applications it can invoke on the same device
The verifier tenant responds with a custom URI or universal link. The Verifier Mobile SDK uses this to launch the wallet app directly.
### Presenting request details to the user [#presenting-request-details-to-the-user]
The wallet retrieves the presentation request and displays:
* The credentials requested
* The claims that will be shared
* Whether the relying party is trusted by the Digital Trust Service
* Which of the user’s credentials match the request
The user authenticates and consents to share the requested information.
### Verifying the credential [#verifying-the-credential]
The MATTR VII verifier tenant verifies the credential by checking:
* Validating the digital signature to confirm the data has not been tampered with
* Checking that the credential has not been revoked or suspended, using a revocation list (if applicable)
* Verifying that the credential is currently valid, based on its “valid from” and “valid until” dates
* Ensuring the credential was issued by a trusted issuer, based on information retrieved from a Digital Trust Service
The issuer of the credential is not informed that the presentation has occurred. No data about the verifier, the context of use, or the interaction itself is shared with the issuer. The only interaction with the issuer is a potential call to an online revocation endpoint, if revocation checking is required.
### Displaying verification results [#displaying-verification-results]
Once verification is complete, the wallet app redirects the user back to the mobile application using the provided redirect URI.
The Verifier Mobile SDK receives the result and displays it within the app interface.
The MATTR VII verifier tenant can also be configured to share the verification
results with a configured back-end rather than the front-end directly.
# Remote mobile verification quickstart guide
URL: /docs/verification/remote-mobile-verifiers/quickstart
This quickstart is for evaluating the
[MATTR Pi mDocs Mobile Verifier SDKs](/docs/verification/remote-mobile-verifiers/sdks/overview). In
about 15-20 minutes you will configure a MATTR VII verifier tenant, run a sample verifier mobile app
(iOS, Android, or React Native) on a physical device, and use it to verify an mDoc presented from a
wallet
application on the same device using the
[remote presentation workflow](/docs/verification/remote-overview) defined by
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html) and
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) Annex B.
This is an app-to-app flow. The verifier app invokes a separate wallet app installed on the same
device to request the credential, and the wallet returns the presentation to the verifier. It is not
the browser-based Digital Credentials API flow: both apps run on the same physical device and
exchange the OID4VP request and response directly between each other.
**Estimated time: 15-20 minutes.** This covers the verifier setup and verification flow. If you are using the sample holder app, allow additional 10-15 minutes to build and run it from the
[Holder SDK quickstart guide](/docs/holding/sdk-quickstart), which you need before [Part 3](#part-3-claim-and-verify-a-credential-5-minutes).
Use this guide as a fast evaluation path to see the Mobile Verifier SDK working end-to-end on a real
device. For detailed information and API examples, explore the
[tutorial](/docs/verification/remote-mobile-verifiers/tutorial) and reference documentation tailored
for each platform.
## User experience [#user-experience]
In this quickstart you will perform this exact flow yourself using a MATTR VII verifier tenant, the
sample verifier app, and a wallet that supports remote presentation acting as the holder:
1. A relying party uses the Mobile Verifier SDK to embed a remote verification workflow into a
mobile application.
2. When a user interacts with the mobile application, a matching wallet application installed on
their mobile device is invoked to request an mDoc for verification.
3. The user consents to sharing the requested information.
4. The user's wallet application shares the matching mDoc with the MATTR VII tenant configured by
the Mobile Verifier SDK to perform the verification workflow.
5. The MATTR VII tenant performs the required checks and returns the verification results via the
Mobile Verifier SDK to the verifier application.
6. The user journey continues based on the verification results.
## Prerequisites [#prerequisites]
* Use the [Get Started form](/docs/resources/get-started) to request a trial of MATTR **verification** capabilities. You will receive access to the following resources:
* MATTR Pi mDocs Verifier SDK for your chosen platform (iOS, Android, or React Native).
* MATTR VII tenant.
* A physical mobile device to run the verifier app, with a wallet that supports remote presentation as
per ISO/IEC 18013-7 and OID4VP installed on the **same device**. You can build and run the sample holder app from the
[Holder SDK quickstart guide](/docs/holding/sdk-quickstart).
* Development environment set up for your chosen platform (Xcode for iOS, Android Studio for Android,
or a React Native / Expo development environment with Node.js 18+).
## Steps [#steps]
In this quickstart you will:
1. Configure a MATTR VII verifier tenant to handle the verification requests.
2. Download, configure, and run a local sample verifier project for your platform.
3. Claim a test credential into a wallet on the same device and verify it remotely.
4. Review the structure of the sample project so you can see how to integrate the SDK into your own
app.
Use the tabs below to follow platform-specific setup steps.
## Part 1: Configure the MATTR VII verifier tenant (5 minutes) [#part-1-configure-the-mattr-vii-verifier-tenant-5-minutes]
These steps configure your verifier tenant so the sample verifier app can request and verify mDocs
issued by a trusted issuer.
### Create a MATTR VII tenant [#create-a-mattr-vii-tenant]
If you already have a tenant you can skip this step.
1. Log into the [MATTR Portal](https://portal.mattr.global).
2. Select the **Create/switch tenant** button on the top-right side of the screen.\
The *All tenants* panel is displayed, listing any existing tenants.
3. Select the **Create new** button.\
The *New tenant* form is displayed.
4. Use the *Region* dropdown list to select the region your tenant will be hosted in.
5. Use the *Tenant subdomain* text box to insert a subdomain for your tenant (e.g. `remote-mobile-verification`).
6. Select the **Create** button to create the new tenant.
7. Copy the displayed tenant information (`audience`, `auth_url`, `tenant_url`, `client_id` and `client_secret`) which is required for the next step.
### Create a verifier application configuration [#create-a-verifier-application-configuration]
Create a Verifier Application on your MATTR VII tenant so the Mobile Verifier SDK can tether to it and request credential presentations:
[Make a request](/docs/api-reference#getting-started) of the following structure to create an iOS Verifier Application configuration on your MATTR VII tenant **(make sure to replace `bundleId`, `teamId` and `redirectUri` with your own values)**:
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "My iOS Mobile Verifier Application",
"type": "ios",
"bundleId": "com.yourname.mobileverifier",
"teamId": "YOUR_APPLE_TEAM_ID",
"appAttest": {
"required": false,
"environment": "development"
},
"openid4vpConfiguration": {
"redirectUri": "com.yourname.mobileverifier://my/path"
}
}
```
* `bundleId`: This must match the bundle identifier of your iOS application you will create in the next step.
* `teamId`: This must match the Apple Developer Team ID that actually signs this app.
* `redirectUri`: The URI the user is redirected to after presenting the credential from their wallet. Structure it as `{your_app_bundleId}://my/path`.
A successful response returns a `201` status code with the created Verifier Application:
```json title="Response"
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c", // [!code focus]
"name": "My iOS Mobile Verifier Application",
"type": "ios",
// ... rest of application configuration
}
```
* `id`: A unique identifier for the Verifier Application (generated by the tenant). You must use this
value when initializing the SDK so that it can correctly identify and authenticate your application.
[Make a request](/docs/api-reference#getting-started) of the following structure to create an Android Verifier Application configuration on your MATTR VII tenant:
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "My Android Mobile Verifier Application",
"type": "android",
"packageName": "com.yourname.mobileverifier",
"packageSigningCertificateThumbprints": [
"1232584B6F6A892D356899FB9576C5F226A179E6199F2B7A1D837B5C234C5A8E"
],
"keyAttestation": {
"required": false
},
"openid4vpConfiguration": {
"redirectUri": "com.yourname.mobileverifier://oid4vp-callback"
}
}
```
A successful response returns a `201` status code with the created Verifier Application:
```json title="Response"
{
"id": "a82bfa46-72a0-4cde-b6cb-2a0de7e2f3c4", // [!code focus]
"name": "My Android Mobile Verifier Application",
"type": "android",
// ... rest of application configuration
}
```
* `id`: A unique identifier for the Verifier Application (generated by the tenant). You must use this
value when initializing the SDK so that it can correctly identify and authenticate your application.
A React Native app runs on both iOS and Android, and the MATTR VII tenant validates requests
differently for each platform. Create a Verifier Application for **each platform you intend to run on**
by following the **iOS** and **Android** tabs above. Each platform's `redirectUri` must match that
platform's URL scheme, so configure one Verifier Application per platform and use the matching
application `id` at runtime.
### Create a supported wallet configuration [#create-a-supported-wallet-configuration]
This defines the URI scheme the MATTR VII tenant uses to invoke the wallet application as part of
the remote verification workflow.
1. Log into your MATTR VII tenant in the [MATTR Portal](https://portal.mattr.global/).
2. Expand the **Credential Verification** menu in the left-hand navigation panel.
3. Select **Supported wallets**.
4. Select **Create new**.
5. Enter a meaningful *Name* for the supported wallet (e.g. "My Supported Wallet").
6. Enter `mdoc-openid4vp://` in the *Authorization Endpoint* field. This is the default OID4VP scheme
used to invoke the wallet application.
7. Select **Create**.
More information on applying different URI schemes and the resulting user experience can be found on
the
[workflow page](/docs/verification/remote-mobile-verifiers/workflow#the-mattr-vii-verifier-tenant-responds-with-a-link-to-invoke-a-matching-application).
### Configure a trusted issuer [#configure-a-trusted-issuer]
In this quickstart you will add a MATTR-provided demo issuer certificate so your verifier accepts
mDocs issued by this issuer.
1. Select **Trusted issuers** under the **Credential Verification** menu.
2. Select **Create new**.
3. Copy and paste the following certificate into the *Certificate PEM file* field:
```
-----BEGIN CERTIFICATE-----
MIICYzCCAgmgAwIBAgIKXhjLoCkLWBxREDAKBggqhkjOPQQDAjA4MQswCQYDVQQG
EwJBVTEpMCcGA1UEAwwgbW9udGNsaWZmLWRtdi5tYXR0cmxhYnMuY29tIElBQ0Ew
HhcNMjQwMTE4MjMxNDE4WhcNMzQwMTE1MjMxNDE4WjA4MQswCQYDVQQGEwJBVTEp
MCcGA1UEAwwgbW9udGNsaWZmLWRtdi5tYXR0cmxhYnMuY29tIElBQ0EwWTATBgcq
hkjOPQIBBggqhkjOPQMBBwNCAASBnqobOh8baMW7mpSZaQMawj6wgM5e5nPd6HXp
dB8eUVPlCMKribQ7XiiLU96rib/yQLH2k1CUeZmEjxoEi42xo4H6MIH3MBIGA1Ud
EwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRFZwEOI9yq
232NG+OzNQzFKa/LxDAuBgNVHRIEJzAlhiNodHRwczovL21vbnRjbGlmZi1kbXYu
bWF0dHJsYWJzLmNvbTCBgQYDVR0fBHoweDB2oHSgcoZwaHR0cHM6Ly9tb250Y2xp
ZmYtZG12LnZpaS5hdTAxLm1hdHRyLmdsb2JhbC92Mi9jcmVkZW50aWFscy9tb2Jp
bGUvaWFjYXMvMjk0YmExYmMtOTFhMS00MjJmLThhMTctY2IwODU0NWY0ODYwL2Ny
bDAKBggqhkjOPQQDAgNIADBFAiAlZYQP95lGzVJfCykhcpCzpQ2LWE/AbjTGkcGI
SNsu7gIhAJfP54a2hXz4YiQN4qJERlORjyL1Ru9M0/dtQppohFm6
-----END CERTIFICATE-----
```
4. Select **Add**.
## Part 2: Configure and run the sample verifier app (5-10 minutes) [#part-2-configure-and-run-the-sample-verifier-app-5-10-minutes]
In this section you will download, configure, and run a sample verifier app that uses the Mobile
Verifier SDK. This sample app is the completed result of the
[remote mobile verification tutorial](/docs/verification/remote-mobile-verifiers/tutorial).
1. Access the sample verifier codebase by either:
* Cloning the [MATTR Sample Apps repository](https://github.com/mattrglobal/sample-apps):
```bash title="Clone sample apps"
git clone https://github.com/mattrglobal/sample-apps.git
```
* Or downloading just the iOS sample verifier project using
[download-directory.github.io](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2Fmattrglobal%2Fsample-apps%2Ftree%2Fmaster%2Fios-remote-verification-tutorial-sample-app).
2. Use Xcode to open the `.xcodeproj` file in the project's folder. You can find it in the
`sample-apps/ios-remote-verification-tutorial-sample-app` directory.
3. Drag the `MobileCredentialVerifierSDK.xcframework` folder (obtained from MATTR as part of the SDK package) into your project.
4. Configure `MobileCredentialVerifierSDK.xcframework` to [Embed & sign](https://help.apple.com/xcode/mac/current/#/dev51a648b07).
5. Set the bundle identifier you entered in
[Part 1](#part-1-configure-the-mattr-vii-verifier-tenant-5-minutes) for the project in the Xcode
project settings (e.g. `com.yourname.mobileverifier`).
6. Open the `Constants` file and update it with your tenant and application details:
```swift title="Constants"
enum Constants {
static let tenantHost = URL(string: "https://your-tenant.vii.mattr.global")!
static let applicationID = "your-application-id"
}
```
* `tenantHost` : Replace with the URL of your MATTR VII tenant, available in the MATTR Portal
under **Platform Management > Tenant**.
* `applicationID` : Replace with the `id` returned when you created the verifier application in
[Part 1](#create-a-verifier-application-configuration).
7. Run the project on a physical iOS device.
1. Access the sample verifier codebase by either:
* Cloning the [MATTR Sample Apps repository](https://github.com/mattrglobal/sample-apps):
```bash title="Clone sample apps"
git clone https://github.com/mattrglobal/sample-apps.git
```
* Or downloading just the Android sample verifier project using
[download-directory.github.io](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2Fmattrglobal%2Fsample-apps%2Ftree%2Fmaster%2Fandroid-remote-verification-tutorial-sample-app).
2. Open the project in Android Studio. You can find it in the
`sample-apps/android-remote-verification-tutorial-sample-app` directory.
3. Unzip the `mobile-credential-verifier-*version*.zip` file (obtained from MATTR as part of the SDK
package) and copy the unzipped `global` folder into the project's `repo` folder.
4. Sync the project with Gradle files to recognize the new module.
5. Open `app/build.gradle.kts` and replace the placeholder
`applicationId` with the package name you entered in
[Part 1](#part-1-configure-the-mattr-vii-verifier-tenant-5-minutes) (e.g.
`com.yourname.mobileverifier`).
6. Open the `Constants` file and update it with your tenant and application details:
```kotlin title="Constants"
object Constants {
const val TENANT_HOST = "https://your-tenant.vii.mattr.global"
const val APPLICATION_ID = "your-application-id"
}
```
* `TENANT_HOST` : Replace with the URL of your MATTR VII tenant, available in the MATTR Portal
under **Platform Management > Tenant**.
* `APPLICATION_ID` : Replace with the `id` returned when you created the verifier application in
[Part 1](#create-a-verifier-application-configuration).
7. Run the project on a physical Android device.
8. Retrieve your app's signing certificate thumbprint and convert it to the required format:
* Open a terminal inside your project's root folder and run:
```bash title="Retrieve signing certificate"
./gradlew signingReport
```
If you see `permission denied: ./gradlew`, the Gradle wrapper has lost its executable bit (this
happens when the project is downloaded as a zip rather than cloned). Make it executable and run
the command again:
```bash title="Fix wrapper permissions"
chmod +x gradlew
```
* Locate the `SHA-256` value in the `Variant: debug` section, remove all colons (`:`), and
convert all letters to lowercase. The result is the thumbprint you will send to your tenant in
the next step:
```bash title="Example conversion"
echo '91:F7:CB:F9:D6:81:53:1B:C7:A5:8F:B8:33:CC:A1:4D:AB:ED:E5:09:C5' | tr -d ':' | tr 'A-F' 'a-f'
```
This thumbprint is only valid for your debug builds. If you intend to publish your app, repeat
this process with the release signing certificate. Refer to
[Android App Signing](/docs/verification/android-app-signing) for more information.
9. Update your verifier application with the formatted thumbprint by making a request of the following structure. Use the application `id` returned in [Part 1](#create-a-verifier-application-configuration) as the `{applicationId}` path parameter, and set `packageSigningCertificateThumbprints` to the thumbprint you generated in the previous step:
```http title="Request"
PUT /v2/presentations/applications/{applicationId}
```
```json title="Request body"
{
"name": "My Android Mobile Verifier Application",
"type": "android",
"packageName": "com.yourname.mobileverifier",
"packageSigningCertificateThumbprints": [
"91f7cbf9d681531bc7a58fb833cca14dabede509c5"
],
"keyAttestation": {
"required": false
},
"openid4vpConfiguration": {
"redirectUri": "com.yourname.mobileverifier://oid4vp-callback"
}
}
```
The `PUT` request replaces the entire application configuration, so include all the fields you
set when you created the application in [Part 1](#create-a-verifier-application-configuration),
not just the thumbprint.
A successful response returns a `200` status code with the updated Verifier Application:
```json title="Response"
{
"id": "a82bfa46-72a0-4cde-b6cb-2a0de7e2f3c4",
"name": "My Android Mobile Verifier Application",
"type": "android",
"packageSigningCertificateThumbprints": [
"91f7cbf9d681531bc7a58fb833cca14dabede509c5"
],
// ... rest of application configuration
}
```
1. Access the sample verifier codebase by either:
* Cloning the [MATTR Sample Apps repository](https://github.com/mattrglobal/sample-apps):
```bash title="Clone sample apps"
git clone https://github.com/mattrglobal/sample-apps.git
```
* Or downloading just the React Native sample verifier project using
[download-directory.github.io](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2Fmattrglobal%2Fsample-apps%2Ftree%2Fmaster%2Freact-native-remote-verification-tutorial%2Freact-native-remote-verification-tutorial-complete).
2. Open the project in your code editor. You can find it in the
`sample-apps/react-native-remote-verification-tutorial/react-native-remote-verification-tutorial-complete`
directory.
3. Open `app.config.ts` and set the app identifiers, each under its marker comment, to the bundle ID /
package name you entered in
[Part 1](#part-1-configure-the-mattr-vii-verifier-tenant-5-minutes) (e.g.
`com.yourname.mobileverifier`):
* Update the `bundleIdentifier` (iOS) value under the `// Update the bundle identifier` comment:
```ts title="app.config.ts"
bundleIdentifier: "com.yourname.mobileverifier",
```
* Update the `package` (Android) value under the `// Update the package name` comment:
```ts title="app.config.ts"
package: "com.yourname.mobileverifier",
```
* Add the iOS custom URL scheme under the `// Update the scheme property` comment. This registers the scheme the wallet uses to redirect back to your app on iOS devices. Set it to your iOS bundle identifier:
```ts title="app.config.ts"
scheme: "com.yourname.mobileverifier",
```
4. Open the `Constants.ts` file and update it with your tenant and application details:
```ts title="Constants.ts"
import { Platform } from "react-native";
/**
* Configuration values used to initialize the SDK and request credentials.
*
* Replace these placeholders with your own values before running the app:
* - `TENANT_HOST`: the URL of your MATTR VII tenant, available in the MATTR Portal under
* Platform Management > Tenant.
* - `IOS_APPLICATION_ID` / `ANDROID_APPLICATION_ID`: the `id` returned when you created the verifier
* application configuration on your MATTR VII tenant (see Part 1).
* iOS and Android each use their own verifier application, because the redirect URI registered on
* the application must match that platform's URL scheme (see `app.config.ts`). Configure one
* application ID per platform.
*/
const IOS_APPLICATION_ID = "your-ios-application-id";
const ANDROID_APPLICATION_ID = "your-android-application-id";
export const Constants = {
TENANT_HOST: "https://your-tenant.vii.mattr.global",
// Resolves to the verifier application ID for the current platform.
APPLICATION_ID: Platform.OS === "ios" ? IOS_APPLICATION_ID : ANDROID_APPLICATION_ID,
};
```
* `TENANT_HOST` : Replace with the URL of your MATTR VII tenant, available in the MATTR Portal under
**Platform Management > Tenant**.
* `IOS_APPLICATION_ID` and `ANDROID_APPLICATION_ID` : Replace the constant for the platform you are
running with the `id` returned when you created the verifier application in
[Part 1](#create-a-verifier-application-configuration). To run on both platforms, create a verifier
application for each (see the callout in
[Part 1](#create-a-verifier-application-configuration)) and set both constants.
* `APPLICATION_ID` : Resolves to the verifier application `id` for the current platform at runtime.
The SDK reads this value when requesting credentials.
5. With the app files configured, install the dependencies:
```bash title="Install dependencies"
yarn install
```
Generate the native project files:
```bash title="Generate project files"
yarn expo prebuild
```
Then run the app on a physical device for your target platform:
```bash title="Run iOS application"
yarn ios --device
```
```bash title="Run Android application"
yarn android --device
```
6. **Android only**: Retrieve your app's signing certificate thumbprint and update the verifier
application configuration:
```bash title="Retrieve signing certificate"
cd android && ./gradlew signingReport
```
Locate the `SHA-256` value in the `Variant: debug` section, remove all colons (`:`), and convert
all letters to lowercase:
```bash title="Example conversion"
echo '91:F7:CB:F9:D6:81:53:1B:C7:A5:8F:B8:33:CC:A1:4D:AB:ED:E5:09:C5' | tr -d ':' | tr 'A-F' 'a-f'
```
Update your verifier application with this thumbprint by making a [`PUT /v2/presentations/applications/{applicationId}`](/docs/verification/remote-verification-api-reference/verifier-applications#update-a-verifier-application) request to your MATTR VII tenant, setting `packageSigningCertificateThumbprints` to the value you just generated (use the Android application `id` returned in [Part 1](#create-a-verifier-application-configuration)).
This thumbprint is only valid for your debug builds. If you intend to publish your app, repeat this
process with the release signing certificate. Refer to
[Android App Signing](/docs/verification/android-app-signing) for more information.
## Part 3: Claim and verify a credential (5 minutes) [#part-3-claim-and-verify-a-credential-5-minutes]
### Claim a credential to present [#claim-a-credential-to-present]
To test the verification flow, you need a compatible mDoc in a wallet that supports remote presentation
on the same device as the verifier app. Use the sample holder app you build with the
[Holder SDK quickstart guide](/docs/holding/sdk-quickstart) to claim a demo mDL credential issued by
the MATTR demo issuer you added as a trusted issuer in [Part 1](#configure-a-trusted-issuer).
1. Build and run the sample holder app on the device running the sample verifier app, following
[Part 1 of the Holder SDK quickstart guide](/docs/holding/sdk-quickstart#part-1-configure-the-sample-holder-project-5-10-minutes).
2. In the sample holder app, select **Claim Credential** (you may need to allow the app to access your
camera).
3. Scan the following QR code:
4. Follow the on-screen instructions to claim the credential into the sample holder app.
This credential will be used in the next step when you test the remote verification flow.
### Verify the credential [#verify-the-credential]
1. Launch the sample verifier app and select **Request credentials**.
2. You will be redirected to the sample holder app, which displays what information is
requested for verification.
3. Select the credential you claimed in the previous step and consent to sharing the requested
information.
4. You will be redirected back to the sample verifier app, which displays the verification results.
Behind the scenes, the Mobile Verifier SDK started a presentation session with your MATTR VII
tenant, invoked the wallet, and surfaced the verification results returned by the tenant according
to OID4VP and ISO/IEC 18013-7:2025 Annex B.
## Review the codebase [#review-the-codebase]
The sample verifier application uses the
[Mobile Verifier SDK](/docs/verification/remote-mobile-verifiers/sdks/overview) to verify mDocs
presented remotely from a wallet on the same device. Once you have the sample application running,
use this section to inspect the key SDK calls you will reuse in your own application.
### Initialize the SDK [#initialize-the-sdk]
The SDK is initialized with your tenant host configuration so it knows which MATTR VII tenant to
interact with.
```swift title="Create platform configuration"
let platformConfiguration = PlatformConfiguration(
tenantHost: URL(string: "https://your-tenant.vii.mattr.global")!,
applicationId: "your-application-id"
)
```
`initialize` is asynchronous from v6.0.0 (call it from an asynchronous context). It registers the app instance with your tenant and obtains a license, so it can throw `failedToRegister` and `invalidLicense`:
```swift title="Initialize the SDK"
mobileCredentialVerifier = MobileCredentialVerifier.shared
try await mobileCredentialVerifier.initialize(platformConfiguration: platformConfiguration)
```
* `tenantHost` : URL of your MATTR VII tenant.
* `applicationId` : The `id` returned when you created the verifier application configuration. Supplied here via `PlatformConfiguration` (no longer passed to `requestMobileCredentials`) and used for [SDK Tethering](/docs/verification/sdks/sdk-tethering).
```kotlin title="Create platform configuration"
val platformConfiguration = PlatformConfiguration(
tenantHost = URL("https://your-tenant.vii.mattr.global"),
applicationId = "your-application-id"
)
```
`initialize` registers the app instance with your tenant and obtains a license, so it can throw `FailedToRegisterException` and `InvalidLicenseException`:
```kotlin title="Initialize the SDK"
MobileCredentialVerifier.initialize(context, platformConfiguration)
```
* `tenantHost` : URL of your MATTR VII tenant.
* `applicationId` : The `id` returned when you created the verifier application configuration. Supplied here via `PlatformConfiguration` (no longer passed to `requestMobileCredentials`) and used for [SDK Tethering](/docs/verification/sdks/sdk-tethering).
```tsx title="Create platform configuration and initialize"
import { initialize } from "@mattrglobal/mobile-credential-verifier-react-native";
const result = await initialize({
platformConfiguration: {
tenantHost: "https://your-tenant.vii.mattr.global",
},
});
if (result.isErr()) {
console.error("Failed to initialize SDK:", result.error);
}
```
* `tenantHost` : Base URL of your MATTR VII tenant.
### Create a presentation request [#create-a-presentation-request]
A presentation request defines the type of credential and the specific data elements being requested
for verification.
```swift title="Create a presentation request"
let mobileCredentialRequest = MobileCredentialRequest(
docType: "org.iso.18013.5.1.mDL",
namespaces: [
"org.iso.18013.5.1": [
"family_name": false,
"given_name": false,
"birth_date": false
]
]
)
```
* `docType` : The type of credential being requested (e.g. `org.iso.18013.5.1.mDL`).
* `namespaces` : A dictionary mapping each namespace to its requested attributes. Each attribute is
a key with a Boolean indicating whether the verifier intends to retain this attribute (`true`) or
not (`false`).
```kotlin title="Create a presentation request"
val mobileCredentialRequest = MobileCredentialRequest(
docType = "org.iso.18013.5.1.mDL", namespaces = NameSpaces(
mapOf(
"org.iso.18013.5.1" to DataElements(
mapOf(
"family_name" to false, "given_name" to false, "birth_date" to false
)
)
)
)
)
```
* `docType` : The type of credential being requested (e.g. `org.iso.18013.5.1.mDL`).
* `namespaces` : A map of each namespace to its requested attributes. Each attribute is a key with a
Boolean indicating whether the verifier intends to retain this attribute (`true`) or not
(`false`).
```tsx title="Create a presentation request"
const mobileCredentialRequest = {
docType: "org.iso.18013.5.1.mDL",
namespaces: {
"org.iso.18013.5.1": {
family_name: false,
given_name: false,
birth_date: false,
},
},
};
```
* `docType` : The type of credential being requested (e.g. `org.iso.18013.5.1.mDL`).
* `namespaces` : An object mapping each namespace to its requested attributes. Each attribute is a key
with a Boolean indicating whether the verifier intends to retain this attribute (`true`) or not
(`false`).
### Request credentials from the wallet application [#request-credentials-from-the-wallet-application]
This starts a presentation session with the MATTR VII tenant and redirects the user to a matching
wallet application.
```swift title="Request credentials"
let onlinePresentationResult = try await mobileCredentialVerifier.requestMobileCredentials(
request: [mobileCredentialRequest]
)
```
* `request` : Array of `MobileCredentialRequest` objects defining what information to request.
From v6.0.0, the `applicationId` is no longer passed here — it comes from the `PlatformConfiguration` you set when initializing the SDK. The `challenge` parameter is now optional: omit it to let the SDK generate a secure challenge, or pass your own unique value per session to mitigate replay attacks.
```kotlin title="Request credentials"
val onlinePresentationResult = MobileCredentialVerifier.requestMobileCredentials(
activity = activity,
request = listOf(mobileCredentialRequest)
)
```
* `activity` : Defines the current activity context.
* `request` : List of `MobileCredentialRequest` objects defining what information to request.
From v7.0.0, the `applicationId` is no longer passed here — it comes from the `PlatformConfiguration` you set when initializing the SDK.
```tsx title="Request credentials"
const result = await requestMobileCredentials({
request: [mobileCredentialRequest],
applicationId: Constants.APPLICATION_ID,
challenge: Crypto.randomUUID(),
});
```
* `request` : Array of `MobileCredentialRequest` objects defining what information to request.
* `applicationId` : The `id` returned when you created the verifier application configuration.
* `challenge` : A unique, unpredictable value to identify this presentation session and prevent replay
attacks. Generate a new challenge for every request.
### Handle the redirect from the wallet [#handle-the-redirect-from-the-wallet]
After the wallet presents the credential, the user is redirected back to the verifier app via the
custom URL scheme configured for the sample app in
[Part 2](#part-2-configure-and-run-the-sample-verifier-app-5-10-minutes).
```swift title="Handle redirect URL"
.onOpenURL { url in
// Navigate to response screen
viewModel.navigationPath.append(NavigationState.viewResponse)
viewModel.mobileCredentialVerifier.handleDeepLink(url)
}
```
Add this modifier to your SwiftUI view to navigate to the response screen and pass the redirect URL
to the SDK's `handleDeepLink` method. The SDK processes the response and updates the
`receivedDocuments` variable with the verification results.
The SDK handles the redirect via the `Openid4VpCallbackActivity` declared in your
`AndroidManifest.xml`. Register the callback activity with an intent filter that matches the redirect
URI of your verifier application:
```xml title="AndroidManifest.xml"
```
* `android:scheme` : Must match the redirect custom scheme you defined when you created the verifier
application.
* `android:host` : Must match the redirect host you defined when you created the verifier
application.
Handling the redirect differs by platform:
* **iOS**: Forward the wallet's redirect URL to the SDK with `handleDeepLink` so it can complete the
pending `requestMobileCredentials` call:
```tsx title="Handle redirect URL (iOS)"
useEffect(() => {
if (Platform.OS !== "ios") {
return;
}
const subscription = Linking.addEventListener("url", ({ url }) => {
handleDeepLink({ url });
});
return () => subscription.remove();
}, []);
```
* **Android**: The `withOpenid4VpCallbackActivity` config plugin declares the SDK's
`Openid4VpCallbackActivity` in `AndroidManifest.xml`, bound to `{your_packageName}://oid4vp-callback`.
The SDK handles the redirect automatically, so `requestMobileCredentials` resolves directly and no
additional code is required.
### Handle the presentation response [#handle-the-presentation-response]
The
[`requestMobileCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/mobilecredentialverifier/requestmobilecredentials\(request:challenge:walletproviderid:\))
method returns an `OnlinePresentationSessionResult` object containing the presentation details, any
errors encountered, and the verification status of the credentials. The sample app reads the
credentials from this result, stores them, and renders them in a dedicated view.
From v6.0.0, `OnlinePresentationSessionResult` is a `@frozen` enum with `success` and `failure`
cases, so you branch over it with `switch`/`case` instead of inspecting nullable fields:
```swift title="Handle the response"
switch onlinePresentationResult {
case .success(_, _, let mobileCredentialResponse):
receivedDocuments = mobileCredentialResponse?.credentials ?? []
case .failure(_, _, let error):
print("No response received: \(error.message)")
return
}
```
* `success` : Carries `sessionId`, `challenge`, and an optional `mobileCredentialResponse` containing
the verified credentials and their claims.
* `failure` : Carries `sessionId`, `challenge`, and an `error` (`type` and `message`) describing what
went wrong.
* `credentials` : A list of presented credentials with their data, status, and verification results.
The sample app assigns the result to the `receivedDocuments` variable, which a `DocumentView` then
renders. A `DocumentViewModel` converts each `MobileCredentialPresentation` (docType, verification
result, claims, and claim errors) into a human-readable format for display.
The
[`requestMobileCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/m-docs%20-verifier%20-s-d-k/global.mattr.mobilecredential.verifier/-mobile-credential-verifier/request-mobile-credentials.html)
method returns an
[`OnlinePresentationSessionResult`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/m-docs%20-verifier%20-s-d-k/global.mattr.mobilecredential.verifier/-online-presentation-session-result/index.html)
object. This response contains the presentation details provided by the holder, including any errors
encountered and the verification status of the credentials:
From v7.0.0, `OnlinePresentationSessionResult` is a sealed interface with `Success` and `Failure` variants:
```kotlin title="OnlinePresentationSessionResult structure"
sealed interface OnlinePresentationSessionResult {
val sessionId: String
val challenge: String?
data class Success(
override val sessionId: String,
override val challenge: String? = null,
val mobileCredentialResponse: MobileCredentialResponse?
) : OnlinePresentationSessionResult
data class Failure(
override val sessionId: String,
override val challenge: String? = null,
val error: OnlinePresentationResultError
) : OnlinePresentationSessionResult
}
```
Branch over the result with `when`/`is` instead of inspecting nullable fields:
```kotlin title="Handle the response"
when (onlinePresentationResult) {
is OnlinePresentationSessionResult.Failure -> {
println("No response received: ${onlinePresentationResult.error.message}")
}
is OnlinePresentationSessionResult.Success -> {
val receivedCredentials = onlinePresentationResult.mobileCredentialResponse?.credentials.orEmpty()
// Process the received credentials
for (credential in receivedCredentials) {
println("DocType: ${credential.docType}")
println("Verified: ${credential.verificationResult.verified}")
credential.claims?.forEach { (namespace, elements) ->
elements.forEach { (elementId, value) ->
println("$namespace.$elementId: ${value.value}")
}
}
}
}
}
```
* `Success.mobileCredentialResponse` : Contains the verified credentials and their claims.
* `Failure.error` : The error that occurred during the verification process.
* `credentials` : A list of presented credentials with their data, status, and verification results.
The sample app assigns the result to the `_receivedDocuments` state flow, which a `DocumentView`
composable then renders. The composable reads the docType and verification result and flattens
`document.claims` into a list of label and value pairs for display.
```tsx title="Handle the response"
const session = result.value;
if (!session.isSuccess) {
console.error(`Verification session failed: ${session.error.message}`);
return;
}
const response = session.mobileCredentialResponse;
setVerificationResults(response ?? null);
```
* `result.value` : The `OnlinePresentationSessionResult`. Check `session.isSuccess` to distinguish a
successful session from a failed one (`session.error`).
* `mobileCredentialResponse` : Contains the verified credentials and their claims.
* `credentials` : A list of presented credentials with their data, status, and verification results.
The sample app stores the response and renders it in a `VerificationResultsModal`, which reads each
`MobileCredentialPresentation` (docType, verification result, claims, and claim errors) and displays
it in a human-readable format.
## Next steps [#next-steps]
* For your evaluation:
* Note how long it took to configure the tenant, run the sample app, and complete a remote
verification.
* Consider how the sample's presentation request and result handling map to your real verification
use cases (for example, which attributes you would request and how you would display them).
* Explore the detailed [tutorial](/docs/verification/remote-mobile-verifiers/tutorial) for
step-by-step implementation guidance.
* Review the SDK reference documentation for your platform:
* [iOS](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk)
* [Android](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/)
* [React Native](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest)
* Learn more about the [remote verification workflow](/docs/verification/remote-mobile-verifiers/workflow).
# Learn how to build an application that can verify an mDoc from another app on the same device
URL: /docs/verification/remote-mobile-verifiers/tutorial
## Overview [#overview]
In this tutorial you will use the
[mDocs Mobile Verifier SDKs](/docs/verification/remote-mobile-verifiers/sdks/overview) to build an application
that can verify an [mDoc](/docs/concepts/mdocs) presented from a different application on the same device via
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html), as defined in
[ISO 18013-7](https://www.iso.org/standard/91154.html) Annex B.
1. A relying party uses the Mobile Verifier SDK to embed a remote verification workflow into a
mobile application.
2. When a user interacts with the mobile application, a matching wallet application installed on
their mobile device is invoked to request an mDoc for verification.
3. The user consents to sharing the requested information.
4. The user's wallet application shares the matching mDoc with the MATTR VII tenant configured by
the Mobile Verifier SDK to perform the verification workflow.
5. The MATTR VII tenant performs the required checks and returns the verification results via the
Mobile Verifier SDK to the verifier application.
6. The user journey continues based on the verification results.
The result will look something like this:
Similar to the iOS and Android videos, but with a single app running on both platforms.
To achieve this, you will build the following capabilities into your verifier application:
* Initialize the SDK, so that your application can use its functions and classes.
* Request an mDoc for verification from a compliant wallet application.
* Handle the redirect from the wallet application.
* Display the verification results.
## Prerequisites [#prerequisites]
Before you get started, let's make sure you have everything you need.
### Prior knowledge [#prior-knowledge]
* The remote verification workflow described in this tutorial is based on the
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html) specification and
the [ISO 18013-7](https://www.iso.org/standard/91154.html) standard. If you are unfamiliar with
these, refer to the following resources for more information:
* What are [mDocs](/docs/concepts/mdocs)?
* What is [remote verification](/docs/verification/remote-overview)?
* Breakdown of the
[remote mobile app verification workflow](/docs/verification/remote-mobile-verifiers/workflow).
* We assume you have experience developing applications in the relevant programming languages and
frameworks (Swift for iOS and Kotlin for Android).
### Assets [#assets]
* Use the [Get Started form](/docs/resources/get-started) to request a trial of MATTR **verification** capabilities. You will receive access to the following resources:
* MATTR Pi mDocs Verifier SDK for your chosen platform (iOS, Android, or React Native).
* MATTR VII tenant.
* As part of your MATTR Pi SDK onboarding process you will be provided with access to the following resources:
* ZIP file which includes the required framework:
(`MobileCredentialVerifierSDK.xcframework.zip`).
* [Sample Verifier app](https://github.com/mattrglobal/sample-apps/tree/master/ios-remote-verification-tutorial-sample-app):
You can use this app for reference as you work through this tutorial.
This tutorial is only meant to be used with the [latest
version](/docs/verification/sdks/overview#versions)
of the iOS mDocs Verifier SDK.
* Use the [Get Started form](/docs/resources/get-started) to request a trial of MATTR **verification** capabilities. You will receive access to the following resources:
* MATTR Pi mDocs Verifier SDK for your chosen platform (iOS, Android, or React Native).
* MATTR VII tenant.
* As part of your MATTR Pi SDK onboarding process you will be provided with access to the following resources:
* A ZIP file that includes the required library (`mobile-credential-verifier-*version*.zip`).
* [Sample Verifier app](https://github.com/mattrglobal/sample-apps/tree/master/android-remote-verification-tutorial-sample-app):
You can use this app for reference as we work through this tutorial.
This tutorial is only meant to be used with the [latest version](/docs/verification/sdks/overview#versions)
of the Android mDocs Verifier SDK.
* Use the [Get Started form](/docs/resources/get-started) to request a trial of MATTR **verification** capabilities. You will receive access to the following resources:
* MATTR Pi mDocs Verifier SDK for your chosen platform (iOS, Android, or React Native).
* MATTR VII tenant.
* Access to the [@mattrglobal/mobile-credential-verifier-react-native](https://www.npmjs.com/package/@mattrglobal/mobile-credential-verifier-react-native) npm package, provided as part of your MATTR Pi SDK onboarding process.
* [Sample Verifier app](https://github.com/mattrglobal/sample-apps/tree/master/react-native-remote-verification-tutorial/react-native-remote-verification-tutorial-complete):
This tutorial is accompanied by a sample app you can use for reference as you work through it.
This tutorial is only meant to be used with the [latest version](/docs/verification/sdks/overview#versions)
of the React Native mDocs Verifier SDK.
### Development environment [#development-environment]
* [Xcode](https://developer.apple.com/xcode/) setup with either:
* Local build settings if you are developing locally.
* [iOS developer account](https://developer.apple.com/programs/enroll/) if you intend to publish
your app.
* [Android Studio](https://developer.android.com/studio).
* [Node.js](https://nodejs.org/) (version 18 or later) and a package manager such as
[Yarn](https://yarnpkg.com/).
* A [React Native development environment](https://reactnative.dev/docs/set-up-your-environment). This
tutorial uses [Expo](https://docs.expo.dev/) with development builds.
* Platform tooling for the device you intend to target:
* [Xcode](https://developer.apple.com/xcode/) for iOS, with either local build settings or an
[iOS developer account](https://developer.apple.com/programs/enroll/) if you intend to publish your
app.
* [Android Studio](https://developer.android.com/studio) for Android.
### Testing device [#testing-device]
You will need a mobile device to test the workflow with.
Supported iOS device to run the built Verifier application on, setup with:
* Available internet connection
* A wallet application that can present an mDoc remotely as per ISO/IEC 18013-7 and OID4VP. We
recommend using the sample holder app you can build with our
[Holder SDK quickstart guide](/docs/holding/sdk-quickstart).
* Use your testing wallet application to claim an mDoc by scanning the following QR code:
Supported Android device to run the built Verifier application on, setup with:
* Available internet connection
* A wallet application that can present an mDoc remotely as per ISO/IEC 18013-7 and OID4VP. We
recommend using the sample holder app you can build with our
[Holder SDK quickstart guide](/docs/holding/sdk-quickstart).
* Use your testing wallet application to claim an mDoc by scanning the following QR code:
A supported iOS or Android device to run the built Verifier application on, setup with:
* Available internet connection
* A wallet application that can present an mDoc remotely as per ISO/IEC 18013-7 and OID4VP. We
recommend using the sample holder app you can build with our
[Holder SDK quickstart guide](/docs/holding/sdk-quickstart).
* Use your testing wallet application to claim an mDoc by scanning the following QR code:
React Native apps built with Expo development builds must run on a physical device, not a simulator
or emulator, to complete the app-to-app presentation flow with a wallet installed on the same device.
Got everything? Let's get going!
## Overview [#overview-1]
The following diagram depicts the workflow you will build in this tutorial:
1. The user triggers the workflow by interacting with the verifier application.
2. The verifier application uses the embedded Mobile Verifier SDK capabilities to start a
presentation-based verification session with the configured MATTR VII tenant.
3. The MATTR VII tenant responds with a link to invoke a matching wallet application.
4. The verifier application uses the link to invoke a matching wallet application using a redirect.
5. The wallet application makes a request to the MATTR VII tenant to retrieve a request object,
defining what information is requested for verification.
6. The MATTR VII tenant returns the request object to the wallet application.
7. The wallet application (upon user consent) returns an authorization response to the MATTR VII
tenant, which includes the information required for verification.
8. The MATTR VII tenant returns the verification results to the verifier application.
9. The verifier application surfaces the verification results to the user and the interaction
continues.
You will build this workflow in two parts:
1. [Part 1: Setup the MATTR VII Verifier tenant](#part-1-setup-the-mattr-vii-verifier-tenant).
2. [Part 2: Build a mobile application with mDocs verification capabilities](#part-2-build-a-mobile-application-with-mdocs-verification-capabilities).
## Part 1: Setup the MATTR VII Verifier tenant [#part-1-setup-the-mattr-vii-verifier-tenant]
The MATTR VII tenant will be used to interact with your mobile application (generating a
verification request) and the wallet application (presenting an mDoc for verification) as per OID4VP
and ISO/IEC 18013-7 Annex B. To enable this, you must:
1. [Create a MATTR VII tenant](#create-a-mattr-vii-tenant): This is the tenant that will be used to perform the verification workflow.
2. [Create a verifier application configuration](#create-a-verifier-application-configuration):
Define what applications can create verification sessions with the MATTR VII tenant, and how to
handle these requests.
3. [Create a supported wallet configuration](#create-a-supported-wallet-configuration):
Define how to invoke specific wallet applications as part of a remote verification workflow.
4. [Configure a trusted issuer](#configure-a-trusted-issuer): The MATTR VII verifier tenant will
only accept mDocs issued by these trusted issuers.
### Create a MATTR VII tenant [#create-a-mattr-vii-tenant]
If you already have a tenant you can skip this step.
1. Log into the [MATTR Portal](https://portal.mattr.global).
2. Select the **Create/switch tenant** button on the top-right side of the screen.\
The *All tenants* panel is displayed, listing any existing tenants.
3. Select the **Create new** button.\
The *New tenant* form is displayed.
4. Use the *Region* dropdown list to select the region your tenant will be hosted in.
5. Use the *Tenant subdomain* text box to insert a subdomain for your tenant (e.g. `remote-mobile-verification`).
6. Select the **Create** button to create the new tenant.
7. Copy the displayed tenant information (`audience`, `auth_url`, `tenant_url`, `client_id` and `client_secret`) which is required for the next step.
### Create a verifier application configuration [#create-a-verifier-application-configuration]
The iOS and Android Verifier SDKs must be **tethered** to a MATTR VII tenant. On initialization, the SDK registers your app instance with the
tenant and obtains a license, so SDK Tethering must be configured before you initialize the SDK. For
a full explanation of tethering and the capabilities it enables, see [SDK Tethering](/docs/verification/sdks/sdk-tethering).
For remote mobile (app-to-app) verification, the Verifier Application also defines the OID4VP redirect URI used to return the user to your app after the wallet presents the credential.
To enable SDK Tethering and configure the OID4VP redirect URI, create a Verifier Application on your MATTR VII tenant:
Make a request of the following structure to create an iOS Verifier Application configuration on your MATTR VII tenant:
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "My iOS Mobile Verifier Application",
"type": "ios",
"bundleId": "com.yourname.mobileverifier",
"teamId": "YOUR_APPLE_TEAM_ID",
"appAttest": {
"required": false,
"environment": "development"
},
"openid4vpConfiguration": {
"redirectUri": "com.yourname.mobileverifier://my/path"
}
}
```
* `name` : A unique name to identify this Verifier Application.
* `type` : Must be `ios`.
* `bundleId` : The Bundle ID of your iOS app (must match your Xcode project configuration).
* `teamId` : Your Apple Developer Team ID.
* `appAttest` : App Attest configuration for the iOS verifier application:
* `required` : When `true`, the app instance must provide a valid App Attest attestation during
registration and token renewal. When `false`, the app can fall back to assertion-only
authentication. See [Attestation vs Assertion](/docs/verification/sdks/sdk-tethering#attestation-vs-assertion-fall-back) for more details.
* `environment` : The App Attest environment (`development` or `production`). Apple recommends
using `development` for testing and `production` for distribution builds.
* `openid4vpConfiguration` :
* `redirectUri` : The URI the user is redirected to after presenting the credential from their
wallet. The example uses the bundle ID as the scheme, but you can use any custom URL scheme. Only
the scheme must match the custom URL scheme you register in the app; the path segment is
illustrative.
A successful response returns a `201` status code with the created Verifier Application:
```json title="Response"
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c", // [!code focus]
"name": "My iOS Mobile Verifier Application",
"type": "ios",
// ... rest of application configuration
}
```
* `id`: A unique identifier for the Verifier Application (generated by the tenant). You must use this
value when initializing the SDK so that it can correctly identify and authenticate your application.
Make a request of the following structure to create an Android Verifier Application configuration on your MATTR VII tenant:
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "My Android Mobile Verifier Application",
"type": "android",
"packageName": "com.example.mobileverifiertutorial",
"packageSigningCertificateThumbprints": [
"1232584B6F6A892D356899FB9576C5F226A179E6199F2B7A1D837B5C234C5A8E"
],
"keyAttestation": {
"required": false
},
"openid4vpConfiguration": {
"redirectUri": "com.example.mobileverifiertutorial://oid4vp-callback"
}
}
```
* `name`: A unique name to identify this Verifier Application.
* `type`: Must be `android`.
* `packageName`: The package name of your Android application. Must match the package name you will define in your Android project later.
* `packageSigningCertificateThumbprints`: SHA-256 hex-encoded fingerprints of the signing key
certificates used to sign your APK or app bundle. This ensures the tenant only accepts requests
from known and trusted applications. Refer to
[Android app signing](/docs/verification/android-app-signing) for more information. We will update this value later in the tutorial after you generate the signing key and obtain its thumbprint.
* `keyAttestation`: Key Attestation configuration for the Android verifier application:
* `required`: When `true`, the app instance must provide a valid Key Attestation during
registration and token renewal. When `false`, the app can register and renew tokens using
just an authentication assertion. See [Attestation vs Assertion](/docs/verification/sdks/sdk-tethering#attestation-vs-assertion-fall-back) for more details.
* `openid4vpConfiguration`:
* `redirectUri`: The URI the user is redirected to after presenting the credential from their
wallet. Structure it as `{your_app_packageName}://oid4vp-callback`.
A successful response returns a `201` status code with the created Verifier Application:
```json title="Response"
{
"id": "a82bfa46-72a0-4cde-b6cb-2a0de7e2f3c4", // [!code focus]
"name": "My Android Mobile Verifier Application",
"type": "android",
// ... rest of application configuration
}
```
* `id`: A unique identifier for the Verifier Application (generated by the tenant). You must use this
value when initializing the SDK so that it can correctly identify and authenticate your application.
A React Native app runs on both iOS and Android, and the MATTR VII tenant validates requests
differently for each platform. Create a Verifier Application for **each platform you intend to run on**
by following the **iOS** and **Android** tabs above. Each platform's `redirectUri` must match that
platform's URL scheme, so configure one Verifier Application per platform and use the matching
application `id` at runtime.
### Create a supported wallet configuration [#create-a-supported-wallet-configuration]
Verifier applications can define specific wallet applications to accept mDocs from as part of their
verification workflows. The MATTR VII verifier tenant needs to be configured with a specific URI
scheme that will be used to invoke these wallets.
1. Log in to the [MATTR Portal](https://portal.mattr.global/) (if you haven't already).
2. In the navigation panel on the left-hand side, expand the **Credential Verification** menu.
3. Click on **Supported wallets**.
4. Click on **Create new**.
5. Enter a meaningful *Name* for the new supported wallet (e.g. "My Supported Wallet").
6. Enter `mdoc-openid4vp://` in the
*Authorization Endpoint* field. This is the URI scheme that will be
used to invoke the wallet application. More information on applying different URI schemes and the resulting user experience can be found in the [workflow page](/docs/verification/remote-mobile-verifiers/workflow#the-mattr-vii-verifier-tenant-responds-with-a-link-to-invoke-a-matching-application).
7. Click on **Create**.
The `authorizationEndpoint` configured in the example above
(`mdoc-openid4vp://`) is the default OID4VP scheme. While this is technically
redundant, we chose to include this step to explain how to configure this
endpoint for wallet application using different schemes. More information on applying different URI schemes and the resulting user experience can be found in the [workflow page](/docs/verification/remote-mobile-verifiers/workflow#the-mattr-vii-verifier-tenant-responds-with-a-link-to-invoke-a-matching-application).
Make the following request to your MATTR VII tenant to
[create a trusted wallet provider configuration](/docs/verification/remote-verification-api-reference/wallet-providers#create-a-wallet-provider):
```http title="Request"
POST /v2/presentations/wallet-providers
```
```json title="Request body"
{
"name": "My Trusted Wallet Provider",
"openid4vpConfiguration": {
"authorizationEndpoint": "mdoc-openid4vp://"
}
}
```
* `name` : Unique name to identify this trusted wallet provider.
* `authorizationEndpoint` : URI scheme that will be used to invoke the wallet application. More information on applying different URI schemes and the resulting user experience can be found in the [workflow page](/docs/verification/remote-mobile-verifiers/workflow#the-mattr-vii-verifier-tenant-responds-with-a-link-to-invoke-a-matching-application).
The `authorizationEndpoint` configured in the example above
(`mdoc-openid4vp://`) is the default OID4VP scheme. While this is technically
redundant, we chose to include this step to explain how to configure this
endpoint for wallet application using different schemes.
*Response*
```json title="Response body"
{
"id": "99890c34-e4b7-4a23-84d6-e5de57114c00", // [!code focus]
"name": "My Trusted Wallet Provider",
"openid4vpConfiguration": {
"authorizationEndpoint": "mdoc-openid4vp://"
}
}
```
* `id` : You will use this value later to indicate this is the wallet the verifier application
expects to receive mDocs from.
### Configure a trusted issuer [#configure-a-trusted-issuer]
1. In the navigation panel on the left-hand side, expand the **Credential Verification** menu.
2. Click on **Trusted issuers**.
3. Click on **Create new**.
4. Copy and paste the following certificate in the *Certificate PEM file* field:
```
-----BEGIN CERTIFICATE-----
MIICYzCCAgmgAwIBAgIKXhjLoCkLWBxREDAKBggqhkjOPQQDAjA4MQswCQYDVQQG
EwJBVTEpMCcGA1UEAwwgbW9udGNsaWZmLWRtdi5tYXR0cmxhYnMuY29tIElBQ0Ew
HhcNMjQwMTE4MjMxNDE4WhcNMzQwMTE1MjMxNDE4WjA4MQswCQYDVQQGEwJBVTEp
MCcGA1UEAwwgbW9udGNsaWZmLWRtdi5tYXR0cmxhYnMuY29tIElBQ0EwWTATBgcq
hkjOPQIBBggqhkjOPQMBBwNCAASBnqobOh8baMW7mpSZaQMawj6wgM5e5nPd6HXp
dB8eUVPlCMKribQ7XiiLU96rib/yQLH2k1CUeZmEjxoEi42xo4H6MIH3MBIGA1Ud
EwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRFZwEOI9yq
232NG+OzNQzFKa/LxDAuBgNVHRIEJzAlhiNodHRwczovL21vbnRjbGlmZi1kbXYu
bWF0dHJsYWJzLmNvbTCBgQYDVR0fBHoweDB2oHSgcoZwaHR0cHM6Ly9tb250Y2xp
ZmYtZG12LnZpaS5hdTAxLm1hdHRyLmdsb2JhbC92Mi9jcmVkZW50aWFscy9tb2Jp
bGUvaWFjYXMvMjk0YmExYmMtOTFhMS00MjJmLThhMTctY2IwODU0NWY0ODYwL2Ny
bDAKBggqhkjOPQQDAgNIADBFAiAlZYQP95lGzVJfCykhcpCzpQ2LWE/AbjTGkcGI
SNsu7gIhAJfP54a2hXz4YiQN4qJERlORjyL1Ru9M0/dtQppohFm6
-----END CERTIFICATE-----
```
6. Click on **Add**.
You must configure trusted issuers on your MATTR VII verifier tenant, as presented mDocs will only
be verified if they had been issued by a trusted issuer.
This is achieved by providing the PEM certificate of the IACA used by these issuers to sign mDocs.
In production environments the issuer can provide it out of band or you can obtain it via their
[issuer's metadata](/docs/verification/remote-mobile-verifiers/tutorial#configure-a-trusted-issuer).
Make the following request to your MATTR VII tenant to
[configure a truster issuer](/docs/verification/remote-verification-api-reference/trusted-issuers#create-a-trusted-issuer):
```http title="Request"
POST /v2/credentials/mobile/trusted-issuers
```
```json title="Request body"
{
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIICYzCCAgmgAwIBAgIKXhjLoCkLWBxREDAKBggqhkjOPQQDAjA4MQswCQYDVQQG\nEwJBVTEpMCcGA1UEAwwgbW9udGNsaWZmLWRtdi5tYXR0cmxhYnMuY29tIElBQ0Ew\nHhcNMjQwMTE4MjMxNDE4WhcNMzQwMTE1MjMxNDE4WjA4MQswCQYDVQQGEwJBVTEp\nMCcGA1UEAwwgbW9udGNsaWZmLWRtdi5tYXR0cmxhYnMuY29tIElBQ0EwWTATBgcq\nhkjOPQIBBggqhkjOPQMBBwNCAASBnqobOh8baMW7mpSZaQMawj6wgM5e5nPd6HXp\ndB8eUVPlCMKribQ7XiiLU96rib/yQLH2k1CUeZmEjxoEi42xo4H6MIH3MBIGA1Ud\nEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRFZwEOI9yq\n232NG+OzNQzFKa/LxDAuBgNVHRIEJzAlhiNodHRwczovL21vbnRjbGlmZi1kbXYu\nbWF0dHJsYWJzLmNvbTCBgQYDVR0fBHoweDB2oHSgcoZwaHR0cHM6Ly9tb250Y2xp\nZmYtZG12LnZpaS5hdTAxLm1hdHRyLmdsb2JhbC92Mi9jcmVkZW50aWFscy9tb2Jp\nbGUvaWFjYXMvMjk0YmExYmMtOTFhMS00MjJmLThhMTctY2IwODU0NWY0ODYwL2Ny\nbDAKBggqhkjOPQQDAgNIADBFAiAlZYQP95lGzVJfCykhcpCzpQ2LWE/AbjTGkcGI\nSNsu7gIhAJfP54a2hXz4YiQN4qJERlORjyL1Ru9M0/dtQppohFm6\n-----END CERTIFICATE-----"
}
```
* `certificatePem` : This is the IACA identifying the
[MATTR Labs demo issuer](https://montcliff-dmv.mattrlabs.com/dashboard) which issues the
credential referenced in the [Testing device prerequisite](#testing-device).
If you intend to test this tutorial with a credential different than the one
recommended in our [Testing device prerequisite](#testing-device), replace the
`certificatePem` value with your own issuer's IACA.
A successful `201` response indicates that this issuer's certificate was added to your MATTR VII
tenant's trusted issuer's list. This means that mDocs that use this IACA as their root certificate
can be trusted and verified.
## Part 2: Build a mobile application with mDocs verification capabilities [#part-2-build-a-mobile-application-with-mdocs-verification-capabilities]
Now that the MATTR VII verifier tenant is properly configured, you can proceed with the steps
required to embed verification capabilities into your mobile verifier application:
1. [Setup your environment](#step-1-environment-setup): Setup the required infrastructure for your
mobile application.
2. [Initialize the SDK](#step-2-initialize-the-sdk): So that the SDK functions are available in your
mobile application.
3. [Request a credential from wallet application](#step-3-request-a-credential-from-wallet-application):
Build the capability to request an mDoc for verification from a wallet application.
4. [Display verification results](#step-4-display-verification-results):
### Step 1: Environment setup [#step-1-environment-setup]
**Step 1: Create a new project**
Follow the detailed instructions to
[Create a new Xcode Project](https://help.apple.com/xcode/mac/current/#/dev07db0e578) and add your
organization's identifier.
**Step 2: Unzip the dependencies file**
1. Unzip the [`MobileCredentialVerifierSDK.xcframework.zip` file](#assets).
2. Drag the `MobileCredentialVerifierSDK.xcframework` folder into your project.
3. Configure `MobileCredentialVerifierSDK.xcframework` to
[Embed & sign](https://help.apple.com/xcode/mac/current/#/dev51a648b07).
See [Add existing files and folders](https://help.apple.com/xcode/mac/current/#/dev81ce1d383) for
detailed instructions.
This should result in the the following framework being added to your project:
**Step 3: Run the application**
Select **Run** and make sure the application launches with a *“Hello, world!”* text in the middle of
the display, as shown in the following image:
**Step 1: Create a new project**
1. [Create a new Android Studio project](https://developer.android.com/studio/projects/create-project),
using the *Empty Activity* template.
2. Name the project `Mobile Verifier Tutorial`.
3. Set the `Package name` to `com.example.mobileverifiertutorial` (the value you used for the `packageName` when you [created the verifier application](#create-a-verifier-application-configuration) on your MATTR VII tenant).
4. Select *API 24* as the `Minimum SDK` version.
5. Select *Kotlin DSL* as the `Build configuration language`.
5. Click **Finish**.
6. [Sync](https://developer.android.com/build#sync-files) the project with Gradle files.
**Step 2: Add required dependencies**
1. Select the [Project view](https://developer.android.com/studio/projects#ProjectView).
2. Create a new directory named `repo` in your project's folder.
3. Unzip the `mobile-credential-verifier-*version*.zip` file and copy the unzipped
`global` folder into the new `repo` folder.
4. Open the `settings.gradle.kts` file in the `MobileVerifierTutorial` folder and add the following Maven
repository to the `dependencyResolutionManagement.repositories` block:
```kotlin title="settings.gradle.kts"
maven {
url = uri("repo")
}
```
5. Open the `app/build.gradle.kts` file in your app folder and add the following dependencies to the
`dependencies` block:
```kotlin title="app/build.gradle.kts"
implementation("global.mattr.mobilecredential:verifier:7.0.0")
implementation("androidx.navigation:navigation-compose:2.9.0")
```
* The `verifier` dependency version should match the version of the unzipped
`mobile-credential-verifier-*version*.zip` file you copied to the `repo` folder.
* The required `navigation-compose` version may differ based on your version of the IDE, Gradle, and other project dependencies.
6. Open the `gradle/libs.versions.toml` file and pin the following library versions:
```kotlin title="gradle/libs.versions.toml"
coreKtx = "1.18.0"
lifecycleRuntimeKtx = "2.10.0"
```
Newly scaffolded projects can pull in transitive AndroidX libraries (such as
`androidx.core:core-ktx` and `androidx.lifecycle:*`) whose latest versions require a newer
`compileSdk` and Android Gradle plugin than Android Studio scaffolded, which causes AAR
metadata check failures on sync. Pinning these versions keeps them compatible with the
scaffolded toolchain.
7. [Sync](https://developer.android.com/build#sync-files) the project with Gradle files.
8. Open the [Build](https://developer.android.com/studio/run#gradle-console) tab and select `Sync`
to make sure that the project has synced successfully.
**Step 3: Run the application**
1. Connect a [debuggable](https://developer.android.com/studio/debug/dev-options#Enable-debugging)
mobile device to your machine.
2. [Build and run the app](https://developer.android.com/studio/run) on the connected mobile
device. The app should launch with a *“Hello, Android!”* text displayed.
**Step 4: Update app signing certificate**
1. Retrieve your app's signing certificate thumbprint and convert it to the required format:
* Open a terminal inside your project's root folder and run:
```bash title="Retrieve signing certificate"
./gradlew signingReport
```
If you see `permission denied: ./gradlew`, the Gradle wrapper has lost its executable bit (this
happens when the project is downloaded as a zip rather than cloned). Make it executable and run
the command again:
```bash title="Fix wrapper permissions"
chmod +x gradlew
```
* Locate the `SHA-256` value in the `Variant: debug` section, remove all colons (`:`), and
convert all letters to lowercase. The result is the thumbprint you will send to your tenant in
the next step:
```bash title="Example conversion"
echo '91:F7:CB:F9:D6:81:53:1B:C7:A5:8F:B8:33:CC:A1:4D:AB:ED:E5:09:C5' | tr -d ':' | tr 'A-F' 'a-f'
```
This thumbprint is only valid for your debug builds. If you intend to publish your app, repeat
this process with the release signing certificate. Refer to
[Android App Signing](/docs/verification/android-app-signing) for more information.
2. Update your verifier application with the formatted thumbprint by making a request of the following structure. Use the application `id` returned in [Create a verifier application configuration](#create-a-verifier-application-configuration) as the `{applicationId}` path parameter, and set `packageSigningCertificateThumbprints` to the thumbprint you generated in the previous step:
```http title="Request"
PUT /v2/presentations/applications/{applicationId}
```
```json title="Request body"
{
"name": "My Android Mobile Verifier Application",
"type": "android",
"packageName": "com.example.mobileverifiertutorial",
"packageSigningCertificateThumbprints": [
"91f7cbf9d681531bc7a58fb833cca14dabede509c5"
],
"openid4vpConfiguration": {
"redirectUri": "com.example.mobileverifiertutorial://oid4vp-callback"
}
}
```
The `PUT` request replaces the entire application configuration, so include all the fields you
set when you created the application in
[Create a verifier application configuration](#create-a-verifier-application-configuration), not
just the thumbprint.
A successful response returns a `200` status code with the updated Verifier Application:
```json title="Response"
{
"id": "a82bfa46-72a0-4cde-b6cb-2a0de7e2f3c4",
"name": "My Android Mobile Verifier Application",
"type": "android",
"packageSigningCertificateThumbprints": [
"91f7cbf9d681531bc7a58fb833cca14dabede509c5"
],
// ... rest of application configuration
}
```
**Step 1: Access the tutorial starter codebase**
1. Access the tutorial starter codebase by either:
* Cloning the MATTR sample-apps repository:
```bash title="Clone the repository"
git clone https://github.com/mattrglobal/sample-apps.git
```
* Or downloading just the starter directory using the
[download-directory.github.io](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2Fmattrglobal%2Fsample-apps%2Ftree%2Fmaster%2Freact-native-remote-verification-tutorial%2Freact-native-remote-verification-tutorial-starter)
utility.
2. Open the tutorial project in your code editor. You can find it in the
`sample-apps/react-native-remote-verification-tutorial/react-native-remote-verification-tutorial-starter/`
directory.
You can find the completed tutorial code in the
`sample-apps/react-native-remote-verification-tutorial/react-native-remote-verification-tutorial-complete`
directory and use it as a reference as you work through this tutorial.
**Step 2: Configure the app identifiers**
1. Open the `app.config.ts` file and update the `bundleIdentifier` value under the
`// Update the bundle identifier` comment to the iOS bundle ID you used when you
[created the verifier application](#create-a-verifier-application-configuration):
```ts title="app.config.ts"
bundleIdentifier: "global.mattr.learn.rnremoteverifiersampleapp",
```
2. Update the `package` value under the `// Update the package name` comment to the Android package
name you used when you created the verifier application:
```ts title="app.config.ts"
package: "global.mattr.learn.rnremoteverifiersampleapp",
```
3. Add the custom URL scheme under the `// Add the custom URL scheme used for the wallet redirect`
comment. This registers the scheme the wallet uses to redirect back to your app. Set it to your
**iOS bundle identifier**:
```ts title="app.config.ts"
scheme: "global.mattr.learn.rnremoteverifiersampleapp",
```
**Step 3: Configure the app plugins**
Add the following code under the `// Configure the app plugins` comment to register the required
plugin configurations:
```ts title="app.config.ts"
"./withMobileCredentialAndroidVerifierSdk",
"./withOpenid4VpCallbackActivity",
[
"expo-build-properties",
{
android: {
minSdkVersion: 24,
compileSdkVersion: 36,
targetSdkVersion: 34,
kotlinVersion: "2.0.21",
},
},
],
```
These plugin files have already been created in your project root directory:
* `withMobileCredentialAndroidVerifierSdk` adds the Maven repository that hosts the native Android
Verifier SDK.
* `withOpenid4VpCallbackActivity` declares the SDK's OpenID4VP callback activity in
`AndroidManifest.xml`, so the wallet can redirect back to your app on Android. The activity is bound
to `{your_packageName}://oid4vp-callback`.
You can also follow the instructions in the
[mDocs Verifier](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/index.html#md:platform-android)
SDK Docs to perform this platform-specific configuration manually.
**Step 4: Install the dependencies**
1. Open a terminal in the project's root and navigate to the starter project directory:
```bash title="Navigate to the project directory"
cd sample-apps/react-native-remote-verification-tutorial/react-native-remote-verification-tutorial-starter/
```
2. Install the application dependencies:
```bash title="Install dependencies"
yarn install
```
**Step 5: Generate the iOS and Android project files**
Run the following command to generate the native project files:
```bash title="Generate project files"
yarn expo prebuild
```
You should now see the `ios` and `android` folders in your project root.
**Step 6: Start the application**
Connect your testing device and run the following command for your target platform:
```bash title="Run iOS application"
yarn ios --device
```
```bash title="Run Android application"
yarn android --device
```
The app should launch with a single **Request credentials** button.
**Step 7 (Android only): Update the app signing certificate**
1. From your project root, change into the generated `android` directory and retrieve your
application's signing certificate:
```bash title="Retrieve signing certificate"
cd android && ./gradlew signingReport
```
2. Locate the `SHA-256` value in the `Variant: debug` section, remove all colons (`:`), and convert
all letters to lowercase:
```bash title="Example conversion"
echo '91:F7:CB:F9:D6:81:53:1B:C7:A5:8F:B8:33:CC:A1:4D:AB:ED:E5:09:C5' | tr -d ':' | tr 'A-F' 'a-f'
```
3. Update your Android verifier application with this thumbprint by making a [`PUT /v2/presentations/applications/{applicationId}`](/docs/verification/remote-verification-api-reference/verifier-applications#update-a-verifier-application) request to your MATTR VII tenant, setting `packageSigningCertificateThumbprints` to the value you just generated (use the application `id` you created in [Part 1](#create-a-verifier-application-configuration)).
This thumbprint is only valid for your debug builds. If you intend to publish your app, repeat this
process with the release signing certificate. Refer to
[Android App Signing](/docs/verification/android-app-signing) for more information.
### Step 2: Initialize the SDK [#step-2-initialize-the-sdk]
The first capability you will build into your app is to initialize the SDK so that the app can use
its functions and classes. To achieve this, you need to import the `MobilecredentialVerifierSDK`
framework and then initialize the `MobileCredentialVerifier` class.
1. Open the `ContentView` file in your new project and replace any existing code with the
following:
```swift title="ContentView"
import SwiftUI
// Step 2.3: Import MobileCredentialVerifierSDK
struct ContentView: View {
@State var viewModel: VerifierViewModel = VerifierViewModel()
var body: some View {
NavigationStack(path: $viewModel.navigationPath) {
VStack {
Button("Request credentials") {
viewModel.requestCredentials()
}
.padding()
}
.navigationDestination(for: NavigationState.self) { destination in
switch destination {
case .viewResponse:
presentationResponseView
}
}
}
// Step 4.2: Handle MATTR VII redirect
}
// MARK: Verification Views
var presentationResponseView: some View {
// Step 4.4: Create PresentationResponseView
EmptyView()
}
}
// MARK: VerifierViewModel
@Observable
final class VerifierViewModel {
var navigationPath = NavigationPath()
// Step 2.4: Setup platform configuration
// Step 2.5: Add MobileCredentialVerifier var
// Step 2.6: Initialize the SDK
// Step 3.1: Create MobileCredentialRequest instance
// Step 3.2: Create receivedDocuments variable
}
// MARK: Same Device Verification
extension VerifierViewModel {
func requestCredentials() {
// Step 3.3: Request credentials
}
}
// MARK: - Navigation
enum NavigationState: Hashable {
case viewResponse
}
```
This will serve as the basic structure for your application. You will copy and paste different
code snippets into specific locations to achieve the different functionalities. These locations
are indicated by comments that reference both the section and the step.
We recommend copying and pasting the comment text in Xcode search field (e.g.
`Step 2.3: Import MobileCredentialVerifierSDK`) to easily locate it in the code.
2. Create a new file named `Constants` and paste the following code into it to define constants
which are required to initialize the SDK:
```swift title="Constants"
import Foundation
enum Constants {
static let tenantHost = URL(string: "https://learn.vii.au01.mattr.global")!
static let applicationID = "74f91a0f-5909-43b0-a431-6da2397d1f86"
}
```
* `tenantHost` : Replace with the URL of your
[MATTR VII tenant](#part-1-setup-the-mattr-vii-verifier-tenant).
* `applicationID` : Replace with the `id` returned when you created the MATTR VII
[verifier application](#create-a-verifier-application-configuration).
3. Return to the `ContentView` file and add the following code after the
`Step 2.3: Import MobileCredentialVerifierSDK` comment to import
`MobileCredentialVerifierSDK` and gain access to the SDK capabilities:
```swift title="ContentView"
import MobileCredentialVerifierSDK
```
4. Add the following code under the `Step 2.4: Setup platform configuration` comment to create a
[`PlatformConfiguration`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/platformconfiguration)
instance:
```swift title="ContentView"
let platformConfiguration = PlatformConfiguration(
tenantHost: Constants.tenantHost,
applicationId: Constants.applicationID
)
```
This instance configures the MATTR VII tenant that the SDK will interact with (`tenantHost`) and
the Verifier Application the SDK represents (`applicationId`), based on the constants defined in
the `Constants` file. From v6.0.0, the `applicationId` is supplied here via `PlatformConfiguration`
(it is no longer passed to `requestMobileCredentials`), and it also drives
[SDK Tethering](/docs/verification/sdks/sdk-tethering).
5. Add the following code after the `Step 2.5: Add MobileCredentialVerifier var` comment to
create a variable that will hold the `mobileCredentialVerifier` instance when the SDK is
initialized:
```swift title="ContentView"
var mobileCredentialVerifier: MobileCredentialVerifier
```
6. Add the following code after the `Step 2.6: Initialize the SDK` comment to initialize the
`MobileCredentialVerifier` instance with the parameters defined in the `platformConfiguration`
instance:
```swift title="ContentView"
init() {
mobileCredentialVerifier = MobileCredentialVerifier.shared
Task {
do {
try await mobileCredentialVerifier.initialize(platformConfiguration: platformConfiguration)
} catch MobileCredentialVerifierError.failedToRegister {
// Registration with the MATTR VII tenant failed during SDK Tethering.
// Print the reason so the failure is visible: check connectivity and that
// tenantHost, applicationId, and the app's bundle identifier / team ID match
// the Verifier Application configuration.
print("SDK initialization failed to register:", MobileCredentialVerifierError.failedToRegister)
} catch MobileCredentialVerifierError.invalidLicense {
// The SDK license is missing, invalid, or expired.
print("SDK initialization failed: invalid license")
} catch {
print("SDK initialization failed:", error.localizedDescription)
}
}
}
```
The `initialize` method is asynchronous (called here from a `Task`) and drives SDK Tethering:
on first launch it registers the app instance with your tenant and obtains a license. Network access is required the first
time the SDK initializes and when the license is later renewed.
7. [Run](https://developer.apple.com/documentation/xcode/running-your-app-in-simulator-or-on-a-device)
the app to ensure it compiles successfully.
The first capability you will build into your app is to initialize the SDK so that the app can use
its functions and classes. To achieve this, you need to import the `MobilecredentialVerifierSDK`
framework and then initialize the `MobileCredentialVerifier` class.
1. Open the `MainActivity` file in your new project and replace any existing code with the
following:
```kotlin title="MainActivity"
package com.example.mobileverifiertutorial
import android.app.Activity
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.ViewModel
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
import global.mattr.mobilecredential.verifier.deviceretrieval.devicerequest.DataElements
import global.mattr.mobilecredential.verifier.deviceretrieval.devicerequest.NameSpaces
import global.mattr.mobilecredential.verifier.dto.MobileCredentialPresentation
import global.mattr.mobilecredential.verifier.dto.MobileCredentialRequest
import global.mattr.mobilecredential.verifier.MobileCredentialVerifier
import global.mattr.mobilecredential.verifier.OnlinePresentationSessionResult
import global.mattr.mobilecredential.verifier.platformconfig.PlatformConfiguration
import global.mattr.mobilecredential.verifier.exception.VerifierException.FailedToRegisterException
import global.mattr.mobilecredential.verifier.exception.VerifierException.InvalidLicenseException
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import java.net.URL
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
MobileVerifierTutorialTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Content(
modifier = Modifier.padding(innerPadding)
)
}
}
}
// Step 2.3: Setup platform configuration
// Step 2.4: Initialize the SDK
}
}
@Composable
fun Content(modifier: Modifier = Modifier) {
val activity = (LocalContext.current) as Activity
val viewModel: VerifierViewModel = viewModel()
val documents by viewModel.receivedDocuments.collectAsState()
Column(
modifier = modifier
.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = {
viewModel.requestCredentials(activity)
}) {
Text("Request credentials")
}
LazyColumn(modifier = Modifier.fillMaxWidth()) {
items(documents) { document ->
// Step 4.5: Display received documents
}
}
}
}
@Preview(showBackground = true)
@Composable
fun ContentPreview() {
MobileVerifierTutorialTheme {
Content()
}
}
class VerifierViewModel : ViewModel() {
private val _receivedDocuments =
MutableStateFlow>(emptyList())
val receivedDocuments: StateFlow> = _receivedDocuments
fun requestCredentials(activity: Activity) {
// Step 3.1: Create MobileCredentialRequest instance
viewModelScope.launch {
_receivedDocuments.value = emptyList()
try {
// Step 3.2: Request credentials
// Step 4.2: Handle response
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
```
This will serve as the basic structure for your application. You will copy and paste different
code snippets into specific locations to achieve the different functionalities. These locations
are indicated by comments that reference both the section and the step.
We recommend copying and pasting the comment text in the Android Studio search field (e.g.
`Step 2.3: Setup platform configuration`) to easily locate it in the code.
2. Create a new file named `Constants` and paste the following code, **replacing the values as indicated**, to define constants which are required to initialize the SDK:
```kotlin title="Constants"
package com.example.mobileverifiertutorial
object Constants {
const val TENANT_HOST = "https://learn.vii.au01.mattr.global"
const val APPLICATION_ID = "74f91a0f-5909-43b0-a431-6da2397d1f86"
}
```
* `TENANT_HOST` : Replace with the URL of your
[MATTR VII tenant](#part-1-setup-the-mattr-vii-verifier-tenant). This indicates the tenant that the SDK will
interact with.
* `APPLICATION_ID` : Replace with the `id` returned when you created the MATTR VII
[verifier application](#create-a-verifier-application-configuration). This indicates the verifier application that the SDK will
represent.
3. Add the following code under the `Step 2.3: Setup platform configuration` comment to create a
[`PlatformConfiguration`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/m-docs%20-verifier%20-s-d-k/global.mattr.mobilecredential.verifier.platformconfig/-platform-configuration/index.html)
instance:
```kotlin title="MainActivity"
val platformConfiguration = PlatformConfiguration(
tenantHost = URL(Constants.TENANT_HOST),
applicationId = Constants.APPLICATION_ID
)
```
This instance configures the MATTR VII tenant that the SDK will interact with (`tenantHost`) and
the Verifier Application the SDK represents (`applicationId`), based on the constants defined in
the `Constants` file.
4. Add the following code after the `Step 2.4: Initialize the SDK` comment to initialize the
`MobileCredentialVerifier` instance with the parameters defined in the `platformConfiguration`
instance:
```kotlin title="MainActivity"
lifecycleScope.launch {
try {
MobileCredentialVerifier.initialize(
context = this@MainActivity, platformConfiguration = platformConfiguration
)
} catch (e: FailedToRegisterException) {
// Registration with the MATTR VII tenant failed during SDK Tethering.
// Log the reason so the failure is visible: check connectivity and that
// tenantHost, applicationId, and the app's package name / signing certificate
// thumbprint match the Verifier Application configuration.
Log.e("VerifierTutorial", "SDK initialization failed to register", e)
} catch (e: InvalidLicenseException) {
// The SDK license is missing, invalid, or expired.
Log.e("VerifierTutorial", "SDK initialization failed: invalid license", e)
}
}
```
5. [Run](https://developer.android.com/studio/run) the app to ensure it compiles successfully.
The first capability you will build into your app is to initialize the SDK so that the app can use
its functions and classes. To achieve this, you import the SDK and call `initialize` with your tenant
host configuration.
1. Open the `App.tsx` file in your project and replace the existing code with the following skeleton
structure:
```tsx title="App.tsx"
import {
type MobileCredentialResponse,
handleDeepLink,
initialize,
requestMobileCredentials,
} from "@mattrglobal/mobile-credential-verifier-react-native";
// import { VerificationResultsModal } from "./VerificationResultsModal";
import * as Crypto from "expo-crypto";
import { StatusBar } from "expo-status-bar";
import { useEffect, useState } from "react";
import { ActivityIndicator, Alert, Linking, Platform, SafeAreaView, Text, TouchableOpacity, View } from "react-native";
import { Constants } from "./Constants";
import { styles } from "./styles";
export default function App() {
// State variables for SDK initialization, UI, and loading messages.
const [isSDKInitialized, setIsSDKInitialized] = useState(false);
const [loadingMessage, setLoadingMessage] = useState(false);
const [verificationResults, setVerificationResults] = useState(null);
const [showVerificationResults, setShowVerificationResults] = useState(false);
// Step 2: Initialize the SDK
// Step 4.1: Handle the wallet redirect (iOS)
// Step 3: Request credentials
return (
mDocs Remote Verifier
{loadingMessage ? (
{loadingMessage}
) : (
{/* Step 3: Add the Request credentials button */}
{!isSDKInitialized && SDK not initialized. Please restart the app.}
)}
{/* Step 4.3: Display the verification results */}
);
}
```
This will serve as the basic structure for your application. You will add code to specific
locations to achieve the different functionalities. These locations are indicated by comments that
reference the step.
We recommend using your editor's search functionality to locate comments like
`// Step 2: Initialize the SDK` when adding new code.
2. Create a new file named `Constants.ts` and paste the following code, **replacing the values as
indicated**, to define the constants required to initialize the SDK and request credentials:
```ts title="Constants.ts"
import { Platform } from "react-native";
/**
* Configuration values used to initialize the SDK and request credentials.
*
* Replace these placeholders with your own values before running the app:
* - `TENANT_HOST`: the URL of your MATTR VII tenant, available in the MATTR Portal under
* Platform Management > Tenant.
* - `IOS_APPLICATION_ID` / `ANDROID_APPLICATION_ID`: the `id` returned when you created the verifier
* application configuration on your MATTR VII tenant (see Part 1).
* iOS and Android each use their own verifier application, because the redirect URI registered on
* the application must match that platform's URL scheme (see `app.config.ts`). Configure one
* application ID per platform.
*/
const IOS_APPLICATION_ID = "your-ios-application-id";
const ANDROID_APPLICATION_ID = "your-android-application-id";
export const Constants = {
TENANT_HOST: "https://your-tenant.vii.mattr.global",
// Resolves to the verifier application ID for the current platform.
APPLICATION_ID: Platform.OS === "ios" ? IOS_APPLICATION_ID : ANDROID_APPLICATION_ID,
};
```
* `TENANT_HOST` : Replace with the URL of your
[MATTR VII tenant](#part-1-setup-the-mattr-vii-verifier-tenant). This indicates the tenant that
the SDK will interact with.
* `IOS_APPLICATION_ID` and `ANDROID_APPLICATION_ID` : Create one verifier application per platform
you support, each with a redirect URI that matches that platform's URL scheme. You configure these
schemes in `app.config.ts` in [Step 1: Environment setup](#step-1-environment-setup): iOS uses
`{scheme}://my/path` and Android uses `{package}://oid4vp-callback`. Because the redirect URI on
each verifier application must match the scheme its platform uses, each platform needs its own
application. Paste the iOS application `id` into `IOS_APPLICATION_ID` and the Android application
`id` into `ANDROID_APPLICATION_ID`. Each `id` is the value returned when you created that platform's verifier application in [Part 1](#part-1-setup-the-mattr-vii-verifier-tenant).
* `APPLICATION_ID` : Resolves automatically to the verifier application `id` for the current platform
at runtime. The SDK reads this value when requesting credentials, so you do not need to change
anywhere it is consumed.
3. Return to the `App.tsx` file and add the following code under the `// Step 2: Initialize the SDK`
comment to initialize the SDK with your tenant host:
```tsx title="App.tsx"
useEffect(() => {
const initializeSDK = async () => {
try {
setLoadingMessage("Initializing SDK...");
const result = await initialize({
platformConfiguration: { tenantHost: Constants.TENANT_HOST },
});
if (result.isErr()) {
console.error("Failed to initialize SDK:", result.error);
Alert.alert("Error", "Failed to initialize the verifier SDK");
return;
}
setIsSDKInitialized(true);
} catch (error) {
console.error("Failed to initialize SDK:", error);
Alert.alert("Error", "Failed to initialize the verifier SDK");
} finally {
setLoadingMessage(false);
}
};
initializeSDK();
}, []);
```
Unlike the in-person (proximity) flow, the remote flow requires a `tenantHost`: the SDK starts
presentation sessions with this MATTR VII tenant, which performs the verification server-side and
returns the results. The SDK uses a [`Result`](https://github.com/supermacro/neverthrow) type for
expected errors, so you check `result.isErr()` rather than relying on a thrown exception.
4. [Run](https://docs.expo.dev/develop/development-builds/use-development-builds/) the app to ensure
it compiles successfully.
### Step 3: Request a credential from wallet application [#step-3-request-a-credential-from-wallet-application]
Once the SDK is initialized, you can start building the capabilities to request an mDoc for
verification from a wallet application. This is done by:
1. Creating a request object that defines what information is required for verification.
2. Sending this request to the wallet application installed on the device.
3. Redirecting the user to the wallet application to present the requested mDoc.
1) Open the `ContentView` file and add the following code under the
`Step 3.1: Create MobileCredentialRequest instance` comment to create a new
[MobileCredentialRequest](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/mobilecredentialrequest)
instance:
```swift title="ContentView"
let mobileCredentialRequest = MobileCredentialRequest(
docType: "org.iso.18013.5.1.mDL",
namespaces: [
"org.iso.18013.5.1": [
"family_name": false,
"given_name": false,
"birth_date": false
]
]
)
```
This object defines what information is required for verification:
* The requested credential type (e.g. `org.iso.18013.5.1.mDL`).
* The claims required for verification (e.g. `family_name`).
* The requested namespace (e.g. `org.iso.18013.5.1`).
* Whether or not the verifier intends to persist the claim value (`true`/`false`). Declarative only and not currently enforced by the SDK.
For the verification to be successful, the presented credential must include the referenced
claim against the specific namespace defined in the request. Our example requests the
`birth_date` under the `org.iso.18013.5.1` namespace. If a wallet responds to this request with
a credential that includes a `birth_date` but rather under the `org.iso.18013.5.1.US` namespace,
the claim will not be verified.
2) Add the following code under the `Step 3.2: Create receivedDocuments variable` comment to create
a new `receivedDocuments` variable that will hold the response received from the wallet
application:
```swift title="ContentView"
var receivedDocuments: [MobileCredentialPresentation] = []
```
3) Add the following code under the `Step 3.3: Request credentials` comment to call the SDK's
[`requestMobileCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/mobilecredentialverifier/requestmobilecredentials\(request:challenge:walletproviderid:\))
method:
```swift title="ContentView"
Task { @MainActor in
// Clean the response before fetching a new one
receivedDocuments = []
do {
let onlinePresentationResult = try await mobileCredentialVerifier.requestMobileCredentials(request: [mobileCredentialRequest])
// From v6.0.0, OnlinePresentationSessionResult is a @frozen enum with
// success and failure cases.
switch onlinePresentationResult {
case .success(_, _, let mobileCredentialResponse):
receivedDocuments = mobileCredentialResponse?.credentials ?? []
case .failure(_, _, let error):
print("No response received: \(error.message)")
return
}
} catch {
print(error.localizedDescription)
}
}
```
The following parameter is passed to the `requestMobileCredentials` method:
* `request` : Defines what information to request. This example is passing the
`mobileCredentialRequest` instance you created in the previous step.
From v6.0.0, the `applicationId` is no longer passed here — it is supplied via
`PlatformConfiguration` when you [initialize the SDK](#step-2-initialize-the-sdk). The `challenge`
parameter is now optional: when omitted, the SDK generates a cryptographically secure challenge
for you. You can still pass your own `challenge` (a unique, unpredictable value per session) to
mitigate replay attacks if you prefer to manage it yourself.
4) Run the app and press `Request credentials` button.
You will be redirected to a compliant wallet application, where you will see the verification
request details and choose what mDoc to present for verification.
Once you send the response from the wallet nothing will happen, which is expected at this stage. In
the next step you will build the capability to redirect the user back to the verifier application
and handle the response from the wallet.
1. Open the `MainActivity` file and add the following code under the
`Step 3.1: Create MobileCredentialRequest instance` comment to create a new
[MobileCredentialRequest](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/m-docs%20-verifier%20-s-d-k/global.mattr.mobilecredential.verifier.dto/-mobile-credential-request/index.html?query=data%20class%20MobileCredentialRequest\(val%20docType:%20DocType,%20val%20namespaces:%20NameSpaces\))
instance:
```kotlin title="MainActivity"
val mobileCredentialRequest = MobileCredentialRequest(
docType = "org.iso.18013.5.1.mDL", namespaces = NameSpaces(
mapOf(
"org.iso.18013.5.1" to DataElements(
mapOf(
"family_name" to false, "given_name" to false, "birth_date" to false
)
)
)
)
)
```
This object defines what information is required for verification:
* The requested credential type (e.g. `org.iso.18013.5.1.mDL`).
* The claims required for verification (e.g. `family_name`).
* The requested namespace (e.g. `org.iso.18013.5.1`).
* Whether or not the verifier intends to persist the claim value (`true`/`false`). Declarative only and not currently enforced by the SDK.
For the verification to be successful, the presented credential must include the referenced
claim against the specific namespace defined in the request. Our example requests the
`birth_date` under the `org.iso.18013.5.1` namespace. If a wallet responds to this request with
a credential that includes a `birth_date` but rather under the `org.iso.18013.5.1.US` namespace,
the claim will not be verified.
2. Add the following code under the `Step 3.2: Request credentials` comment to call the SDK's
[`requestMobileCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/m-docs%20-verifier%20-s-d-k/global.mattr.mobilecredential.verifier/-mobile-credential-verifier/request-mobile-credentials.html)
method:
```kotlin title="MainActivity"
val onlinePresentationResult = MobileCredentialVerifier.requestMobileCredentials(
activity = activity,
request = listOf(mobileCredentialRequest)
)
```
The following parameters are passed to the `requestMobileCredentials` method:
* `activity` : Defines the current activity context.
* `request` : Defines what information to request. This example is passing the
`mobileCredentialRequest` instance you created in the previous step.
From v7.0.0, `requestMobileCredentials` no longer takes an `applicationId` argument — the SDK uses
the `applicationId` you supplied via `PlatformConfiguration` when you
[initialized the SDK](#step-2-initialize-the-sdk).
3. Run the app and press `Request credentials` button.
You will be redirected to a compliant wallet application, where you will see the verification
request details and choose what mDoc to present for verification.
Once you send the response from the wallet nothing will happen, which is expected at this stage. In
the next step you will build the capability to redirect the user back to the verifier application
and handle the response from the wallet.
1. Open the `App.tsx` file and add the following code under the `// Step 3: Request credentials`
comment to create a function that builds a request and starts a remote presentation session:
```tsx title="App.tsx"
const requestCredentials = async () => {
try {
setVerificationResults(null);
setLoadingMessage("Requesting credentials...");
// Define what information to request:
// - docType: the requested credential type (org.iso.18013.5.1.mDL)
// - namespaces: the requested namespace (org.iso.18013.5.1) and claims
// - Each claim value (false) indicates the verifier does NOT intend to retain the data.
const mobileCredentialRequest = {
docType: "org.iso.18013.5.1.mDL",
namespaces: {
"org.iso.18013.5.1": {
family_name: false,
given_name: false,
birth_date: false,
},
},
};
const result = await requestMobileCredentials({
request: [mobileCredentialRequest],
applicationId: Constants.APPLICATION_ID,
challenge: Crypto.randomUUID(),
});
if (result.isErr()) {
throw new Error(`Failed to request credentials: ${result.error.message}`);
}
const session = result.value;
if (!session.isSuccess) {
throw new Error(`Verification session failed: ${session.error.message}`);
}
const response = session.mobileCredentialResponse;
if (!response) {
throw new Error("No verification results were returned by the tenant.");
}
setVerificationResults(response);
setShowVerificationResults(true);
} catch (error) {
console.error("Error requesting credentials:", error);
Alert.alert("Error", error instanceof Error ? error.message : String(error));
} finally {
setLoadingMessage(false);
}
};
```
The following parameters are passed to the
[`requestMobileCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/functions/requestMobileCredentials.html)
method:
* `request` : Defines what information to request. This example passes the `mobileCredentialRequest`
object you defined above. For the verification to be successful, the presented credential must
include the referenced claims against the specific namespace defined in the request.
* `applicationId` : Identifier of the verifier application that will be used to verify the request.
In this example it is retrieved from the `Constants` file.
* `challenge` : A unique, unpredictable value generated for each verification session to mitigate
replay attacks. This example uses `Crypto.randomUUID()` from `expo-crypto`. Always generate a new
challenge for every request.
2. Add the following code under the `{/* Step 3: Add the Request credentials button */}` comment to
add a button that invokes the `requestCredentials` function:
```tsx title="App.tsx"
Request credentials
```
3. Run the app and press the `Request credentials` button.
You will be redirected to a compliant wallet application, where you will see the verification
request details and choose what mDoc to present for verification.
Once you send the response from the wallet nothing will happen, which is expected at this stage. In
the next step you will build the capability to handle the response from the wallet and display the
verification results.
### Step 4: Display verification results [#step-4-display-verification-results]
Once the user provides their consent to share the requested information, the wallet application will
send the response back to the MATTR VII tenant, which will then return the verification results to
your verifier application and redirect the user back to the configured redirect URI. In this part of
the tutorial you will build the capability to handle this redirect and display the verification
results in your application.
To enable the redirect back to your verifier application you must register a redirection link. This
could be either a
[Universal link](https://developer.apple.com/documentation/xcode/allowing-apps-and-websites-to-link-to-your-content/)
or a
[custom URL scheme](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app).
In this tutorial you will use a custom URL scheme.
1. Register a custom URL scheme in your verifier application:
* Open the project view and select your application target.
* Select the *Info* tab.
* Scroll down and expand the URL Types area.
* Select the plus button.
* Insert your app bundle identifier (as set when you [configured the MATTR VII verifier application](#create-a-verifier-application-configuration)) in both the *Identifier* and *URL Schemes*
fields.
2. In your `ContentView` file, add the following code under the
`Step 4.2: Handle MATTR VII redirect` comment to handle the redirect from the wallet
application:
```swift title="ContentView"
.onOpenURL { url in
// Navigate to response screen
viewModel.navigationPath.append(NavigationState.viewResponse)
viewModel.mobileCredentialVerifier.handleDeepLink(url)
}
```
This will pass the redirect URL to the SDK's
[`handleDeepLink`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/mobilecredentialverifier/handledeeplink\(_:\))
method, which will process the response from the wallet application and update the
`receivedDocuments` variable with the verification results.
3. Create a new file named `DocumentView` and add the following code to display the retrieved
verification results:
```swift title="DocumentView"
import MobileCredentialVerifierSDK
import SwiftUI
struct DocumentView: View {
var viewModel: DocumentViewModel
var body: some View {
VStack(alignment: .leading, spacing: 10) {
Text(viewModel.docType)
.font(.title)
.fontWeight(.bold)
.padding(.bottom, 5)
Text(viewModel.verificationResult)
.font(.title)
.fontWeight(.bold)
.foregroundStyle(viewModel.verificationFailedReason == nil ? .green : .red)
.padding(.bottom, 5)
if let verificationFailedReason = viewModel.verificationFailedReason {
Text(verificationFailedReason)
.font(.title3)
.fontWeight(.bold)
.foregroundStyle(.red)
.padding(.bottom, 5)
}
ForEach(viewModel.namespacesAndClaims.keys.sorted(), id: \.self) { key in
VStack(alignment: .leading, spacing: 5) {
Text(key)
.font(.headline)
.padding(.vertical, 5)
.padding(.horizontal, 10)
.background(Color.gray.opacity(0.2))
.cornerRadius(5)
ForEach(viewModel.namespacesAndClaims[key]!.keys.sorted(), id: \.self) { claim in
HStack {
Text(claim)
.fontWeight(.semibold)
Spacer()
Text(viewModel.namespacesAndClaims[key]![claim]! ?? "")
.fontWeight(.regular)
}
.padding(.vertical, 5)
.padding(.horizontal, 10)
.background(Color.white)
.cornerRadius(5)
.shadow(radius: 1)
}
}
.padding(.vertical, 5)
}
if !viewModel.claimErrors.isEmpty {
Text("Failed Claims:")
.font(.headline)
.padding(.vertical, 5)
ForEach(viewModel.claimErrors.keys.sorted(), id: \.self) { key in
VStack(alignment: .leading, spacing: 5) {
Text(key)
.font(.headline)
.padding(.vertical, 5)
.padding(.horizontal, 10)
.background(Color.gray.opacity(0.2))
.cornerRadius(5)
ForEach(viewModel.claimErrors[key]!.keys.sorted(), id: \.self) { claim in
HStack {
Text(claim)
.fontWeight(.semibold)
Spacer()
Text(viewModel.claimErrors[key]![claim]! ?? "")
.fontWeight(.regular)
}
.padding(.vertical, 5)
.padding(.horizontal, 10)
.background(Color.white)
.cornerRadius(5)
.shadow(radius: 1)
}
}
.padding(.vertical, 5)
}
}
}
.padding()
.background(RoundedRectangle(cornerRadius: 10).fill(Color.white).shadow(radius: 5))
.padding(.horizontal)
}
}
// MARK: DocumentViewModel
@Observable
class DocumentViewModel {
let docType: String
let namespacesAndClaims: [String: [String: String?]]
let claimErrors: [String: [String: String?]]
let verificationResult: String
let verificationFailedReason: String?
init(from presentation: MobileCredentialPresentation) {
self.docType = presentation.docType
self.verificationResult = presentation.verificationResult.verified ? "Verified" : "Invalid"
self.verificationFailedReason = presentation.verificationResult.failureType?.rawValue
self.namespacesAndClaims = presentation.claims?.reduce(into: [String: [String: String]]()) { result, outerElement in
let (outerKey, innerDict) = outerElement
result[outerKey] = innerDict.mapValues { $0.textRepresentation }
} ?? [:]
self.claimErrors = presentation.claimErrors?.reduce(into: [String: [String: String]]()) { result, outerElement in
let (outerKey, innerDict) = outerElement
result[outerKey] = innerDict.mapValues { "\($0)" }
} ?? [:]
}
}
// MARK: Helper
extension MobileCredentialElementValue {
var textRepresentation: String {
switch self {
case .bool(let bool):
return "\(bool)"
case .string(let string):
return string
case .int(let int):
return "\(int)"
case .unsigned(let uInt):
return "\(uInt)"
case .float(let float):
return "\(float)"
case .double(let double):
return "\(double)"
case let .date(date):
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .none
return dateFormatter.string(from: date)
case let .dateTime(date):
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .short
return dateFormatter.string(from: date)
case .data(let data):
return "Data \(data.count) bytes"
case .map(let dictionary):
let result = dictionary.mapValues { value in
value.textRepresentation
}
return "\(result)"
case .array(let array):
return array.reduce("") { partialResult, element in
partialResult + element.textRepresentation
}
.appending("")
@unknown default:
return "Unknown type"
}
}
}
```
The `DocumentView` file comprises the following elements:
* `DocumentView` : Basic UI layout for viewing received documents and verification results.
* `DocumentViewModel` : This class takes `MobileCredentialPresentation` and converts its
elements into strings that are displayed in the `DocumentView`.
* Extension of `MobileCredentialElementValue` which converts the values of received claims into
a human-readable format.
4. Return to the `ContentView` file and replace the `EmptyView()` under the
`Step 4.4: Create PresentationResponseView` comment with the following code to display the
`DocumentView` view when verification results are available:
```swift title="ContentView"
ZStack {
if viewModel.receivedDocuments.isEmpty {
VStack(spacing: 40) {
Text("Waiting for response...")
.font(.title)
ProgressView()
.progressViewStyle(.circular)
.scaleEffect(2)
}
} else {
ScrollView {
ForEach(viewModel.receivedDocuments, id: \.docType) { doc in
DocumentView(viewModel: DocumentViewModel(from: doc))
.padding(10)
}
}
}
}
```
To enable the redirect back to your verifier application you must register a redirection link. This
could be either
[App Links](https://developer.android.com/training/app-links/about)
or a
[Custom deep links](https://developer.android.com/training/app-links/create-deeplinks).
In this tutorial you will use a custom deep link.
1. In your app's `AndroidManifest.xml` file, add the following activity declaration to register the callback url:
```xml title="AndroidManifest.xml"
```
* `android:scheme` : This must match the package name you defined when you [created the verifier application](#create-a-verifier-application-configuration).
* `android:host` : This must match the redirect host you defined when you [created the verifier application](#create-a-verifier-application-configuration).
2. Add the following code under the `Step 4.2: Handle response` to navigate the
user to the response screen, where they can see the retrieved credentials, if the retrieval was
successful:
```kotlin title="MainActivity.kt"
_receivedDocuments.value = when (onlinePresentationResult) {
is OnlinePresentationSessionResult.Success ->
onlinePresentationResult.mobileCredentialResponse?.credentials ?: emptyList()
is OnlinePresentationSessionResult.Failure -> {
// onlinePresentationResult.error is available here
emptyList()
}
}
```
3. Create a new file named `DocumentView.kt` that will be used to display the response to the
verifier application user.
4. Copy and paste the following code into the new file:
```kotlin title="DocumentView.kt"
package com.example.mobileverifiertutorial
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import global.mattr.mobilecredential.verifier.dto.MobileCredentialPresentation
@Composable
fun DocumentView(document: MobileCredentialPresentation, modifier: Modifier = Modifier) {
val verified: Boolean = document.verificationResult.verified
val statusText: String = if (verified) "Verified" else "Invalid"
val statusColor: Color = if (verified) Color.Green else Color.Red
val flatClaims: List = document.claims?.flatMap { (_, claimsMap) ->
claimsMap.map { (claim, value) -> "$claim: ${value.value}" }
} ?: emptyList()
Card(
modifier = modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(containerColor = Color.White),
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = document.docType,
color = Color.Black,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold,
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = statusText,
color = statusColor,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
)
Spacer(modifier = Modifier.height(12.dp))
if (flatClaims.isEmpty()) {
Text(
text = "No claims",
color = Color.Black,
style = MaterialTheme.typography.labelMedium,
)
} else {
flatClaims.forEach { line ->
Text(
text = line,
color = Color.Black,
style = MaterialTheme.typography.labelMedium
)
}
}
}
}
}
```
5. Back in the `MainActivity.kt` file, add the following code under the
`Step 4.5: Display received documents` comment:
```kotlin title="MainActivity.kt"
DocumentView(document)
```
After the wallet presents the credential, the user is redirected back to your app and the SDK makes
the verification results available. Handling this redirect differs by platform:
* **iOS**: The wallet redirects back to your app via the custom URL scheme you registered in
`app.config.ts`. You must forward the redirect URL to the SDK so it can complete the pending
`requestMobileCredentials` call.
* **Android**: The `withOpenid4VpCallbackActivity` plugin you configured in
[Step 1: Environment setup](#step-1-environment-setup) declared the SDK's
`Openid4VpCallbackActivity` in `AndroidManifest.xml`. The SDK handles the redirect automatically, so
`requestMobileCredentials` resolves directly with the result and no additional code is required.
1. In your `App.tsx` file, add the following code under the
`// Step 4.1: Handle the wallet redirect (iOS)` comment to forward the redirect URL to the SDK on
iOS:
```tsx title="App.tsx"
useEffect(() => {
if (Platform.OS !== "ios") {
return;
}
const subscription = Linking.addEventListener("url", ({ url }) => {
handleDeepLink({ url });
});
return () => subscription.remove();
}, []);
```
This passes the redirect URL to the SDK's
[`handleDeepLink`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/functions/handleDeepLink.html)
method, which processes the response from the wallet and resolves the pending
`requestMobileCredentials` call so it returns the verification results.
2. Create a new file named `VerificationResultsModal.tsx` and paste the following code to display the
retrieved verification results:
```tsx title="VerificationResultsModal.tsx"
import type { MobileCredentialResponse } from "@mattrglobal/mobile-credential-verifier-react-native";
import { Modal, SafeAreaView, ScrollView, Text, TouchableOpacity, View } from "react-native";
import { styles } from "./styles";
interface VerificationResultsModalProps {
visible: boolean;
onClose: () => void;
verificationResults: MobileCredentialResponse | null;
}
export function VerificationResultsModal({ visible, onClose, verificationResults }: VerificationResultsModalProps) {
if (!visible || !verificationResults) return null;
// mDoc claims can have various types (string, number, date, array, object, etc.).
// Arrays and objects are serialized to JSON; all other types use String conversion.
function renderClaimValue(claim: any): string {
if (!claim) return "undefined";
if (claim.type === "array" || claim.type === "object") {
return JSON.stringify(claim.value);
}
return String(claim.value);
}
return (
Verification ResultsClose
{verificationResults.credentials && verificationResults.credentials.length > 0 ? (
{/* Overall verification status for the first credential, as determined by the tenant. */}
{verificationResults.credentials[0].verificationResult?.verified
? "✓ Verified"
: "✗ Verification Failed"}
{verificationResults.credentials[0].docType}
{/* Claims organized by namespace. */}
{verificationResults.credentials.map((credential, credIndex) => (
{credential.claims &&
Object.keys(credential.claims).map((namespace, nsIndex) => (
{namespace}
{credential.claims &&
Object.entries(credential.claims[namespace]).map(([key, value], idx) => (
{key}:{renderClaimValue(value)}
))}
))}
{/* Verification failure reason, if the mDoc did not pass verification. */}
{!credential.verificationResult?.verified && credential.verificationResult?.reason && (
Verification Failed:Type: {credential.verificationResult.reason.type}Message: {credential.verificationResult.reason.message}
)}
{/* Claim errors: claims that were requested but not provided. */}
{credential.claimErrors && Object.keys(credential.claimErrors).length > 0 && (
Claim Errors
{Object.entries(credential.claimErrors).map(([namespace, errors]) =>
Object.entries(errors).map(([elementId, errorCode]) => (
{namespace}.{elementId}:
Error: {errorCode}
))
)}
)}
))}
) : (
No data available
)}
);
}
```
This component reads each `MobileCredentialPresentation` from the `MobileCredentialResponse` and
renders the docType, the overall verification result, the received claims grouped by namespace, and
any claim errors.
3. Back in `App.tsx`, uncomment the `VerificationResultsModal` import at the top of the file:
```tsx title="App.tsx"
import { VerificationResultsModal } from "./VerificationResultsModal";
```
4. Add the following code under the `{/* Step 4.3: Display the verification results */}` comment to
render the modal when results are available:
```tsx title="App.tsx"
setShowVerificationResults(false)}
verificationResults={verificationResults}
/>
```
## Test the end-to-end workflow [#test-the-end-to-end-workflow]
1. Run the app.
2. Select the **Request credentials** button.\
You should be redirected to a compliant wallet application, where you will see the verification
request details and choose what mDoc to present for verification.
3. Use the wallet application to present the requested mDoc.\
You will be redirected back to the verifier application where you will see the verification
results.
You should see a result similar to the following:
The React Native app follows the same end-to-end flow as the iOS and Android apps shown above: the
verifier app starts a presentation session, redirects to a compliant wallet, and displays the
verification results returned by the MATTR VII tenant once the user consents to share the requested
information.
1. The verifier app starts a presentation session and gets redirected.
2. The user is redirected to a compliant wallet application.
3. The user provides their consent to share the requested information.
4. The wallet application sends the response back to the MATTR VII tenant.
5. The MATTR VII tenant redirects the user back to the verifier app with the verification results.
6. The verifier app fetches the result and presents the result to user.
Congratulations! Your verifier application can now verify mDocs presented from a compliant wallet
installed on the same mobile device.
## Summary [#summary]
You have just used the [mDocs Mobile Verifier SDKs](/docs/verification/remote-mobile-verifiers/sdks/overview) to
build an application that can verify an [mDoc](/docs/concepts/mdocs) presented from a compliant wallet on the
same device using a remote presentation workflow as per OID4VP and ISO/IEC 18013-7 Annex B.
This was achieved by building the following capabilities into the application:
* Initialize the SDK, so that your application can use its functions and classes.
* Request an mDoc for verification from a compliant wallet application.
* Display verification results in your verifier application.
## What's next? [#whats-next]
* You can check out the SDKs reference documentation to learn more about available functions and
classes:
* [iOS](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk)
* [Android](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/index.html)
* [React Native](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest)
# mDocs remote mobile app verification
URL: /docs/verification/remote-mobile-verifiers/workflow
mDocs are digital credentials based on the ISO/IEC
[18013-5](https://www.iso.org/standard/69084.html) standard and
[18013-7](https://www.iso.org/standard/91154.html) technical specification, designed to be stored on
a holder’s mobile device and support either in-person or remote verification workflows.
The purpose of this page is to describe the end-to-end remote mobile app verification workflow, in
which a user interacts with a verifier mobile app and then uses another mobile app, installed on the
same device, to present an mDoc for verification.
## Prerequisites [#prerequisites]
We recommend you make yourself familiar with the following concepts to support your understanding of
the implementation described in this page:
* What is [credential verification](/docs/verification)?
* What are [mDocs](/docs/concepts/mdocs)?
* What is [remote verification](/docs/verification/remote-overview)?
## Overview [#overview]
Remote mobile app verification of mDocs is based on
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html). Let's use a simple
example to illustrate how such a workflow might look like:
1. A relying party uses the Verifier SDK to embed a remote verification workflow into an existing
mobile verifier application.
For example, a bank embeds remote verification as part of the workflow for opening a new bank
account on their mobile banking app.
2. When a user interacts with the mobile app, a matching wallet application installed on their
mobile device is invoked to request sharing information held in an mDoc.
In our example, the user attempts to open a new bank account in the bank's mobile banking app,
and is requested to provide a supporting identity document, such as a Mobile Drivers License
(mDL).
3. The user consents to sharing the requested information.
In our example, the user has already claimed an mDL as an mDoc into their wallet application in
a previous interaction, and can now share that credential to prove their address.
4. The user's wallet application shares the matching mDoc with the MATTR VII tenant configured by
the Verifier SDK to perform the verification workflow.
5. The MATTR VII tenant performs the required checks and returns the verification results via the
Verifier SDK to the verifier application.
6. The user journey continues based on the verification results and the relying party business logic
built into the verifier application.
In our example, pending successful verification of the mDoc, the user can proceed to opening a
new bank account.
## Detailed workflow [#detailed-workflow]
Let's take a closer look at the end-to-end workflow and explain the role of each component:
### The user triggers the workflow [#the-user-triggers-the-workflow]
The workflow is triggered when the user interacts with a mobile application that requires presenting
information stored in an mDoc.
For example, a user opening a bank account in their banking app may be asked to provide an identity
document. They can do this by presenting an mDoc stored in a wallet application on the same mobile
device.
### The Verifier SDK starts a presentation-based verification session [#the-verifier-sdk-starts-a-presentation-based-verification-session]
The verifier application uses the Verifier SDK to send a request to a MATTR VII verifier tenant to
start a remote, presentation-based verification session.
This request is based on the Verifier SDK configuration that defines:
* The credential query:
* What credentials are required.
* What specific claims are required from these credentials.
* What MATTR VII tenant to interact with.
* A unique identifier of this mobile application.
* What [protocol](/docs/verification/remote-overview#verification-requests) to use for the interaction.
On the MATTR VII side, the tenant is configured to determine how to handle requests from different
[applications](/docs/verification/remote-verification-api-reference/verifier-applications#create-a-verifier-application)
based on the application identifier provided in the request.
### The MATTR VII verifier tenant responds with a link to invoke a matching application [#the-mattr-vii-verifier-tenant-responds-with-a-link-to-invoke-a-matching-application]
The MATTR VII verifier tenant responds with a URI, referred to as the *Authorization endpoint* in
OID4VP. This endpoint is configured when you
[create a trusted wallet provider](/docs/verification/remote-verification-api-reference/wallet-providers#create-a-wallet-provider)
and defines the URI to invoke an application based on its unique identifier.
As per OID4VP, the Authorization endpoint can be one of the following:
* **Default URI scheme**: This is a generic URI scheme defined by the OID4VP specification
(`openid-vp://`) that can be handled by any compliant application.
* If you are interacting with the link from an app that already recognizes this type of scheme, it
can handle the request immediately.
* If you are interacting with the link outside of the app (such as tapping a link in a
browser or scanning a QR code with the native camera), this will typically prompt the OS to
present options of any app that is registered to handle that scheme (on Android), or sometimes it
will automatically pick an app without enabling user selection (on iOS).
This approach is less secure as **any application** registered to handle this scheme could respond
to the request, which might lead to phishing attacks where a malicious app pretends to be a
trusted one.
* **Custom URI**: The [OAuth 2.0 for Native Apps](https://datatracker.ietf.org/doc/html/rfc8252) specification
proposes using more unique custom schemes to make it clearer that the link is intended for a
particular app. This uses reverse-domain schemes, such as `com.example.app://`.
This works in the same way as the default OID4VP custom scheme, but makes it less likely that
another app would be registered to handle the same reverse domain.
* **HTTP link**: Also called out in the [OAuth 2.0 for Native Apps](https://datatracker.ietf.org/doc/html/rfc8252)
specification, the most secure way to ensure only a specific app can handle a link is to register a
domain path to open your app via App Links on Android and Universal Links on iOS.
This is an HTTPS URL pointing to your domain, which the operating system verifies is
registered to only open a particular app. It provides the smoothest user experience as the OS can reliably
open the correct app without presenting choices.
HTTP links are recommended by the OID4VP specification as they are more secure. These URIs are
verified against the domain owner before launching the application, as the domain must host an
association file declaring the app's link. This ensures the provider owns both the app and the
domain.
Additionally, it is recommended to host a webpage for this URI to allow the user to continue the
interaction if the HTTP link fails to invoke the intended application. For example, the user
might not have the application installed and in this case the page should include a link to
enable them to download the app.
HTTP links require domain ownership and proper configuration of verification files on your web server. More information can be found in the following resources:
* [iOS - Universal Links](https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app)
* [Android - App Links](https://developer.android.com/training/app-links/verify-site-associations)
To use a custom URI scheme or App Link/Universal Link, the *Authorization endpoint* must be properly configured on the MATTR VII verifier tenant when creating a trusted wallet provider, for example:
* Custom URI scheme: `com.example.app://openid-vp` (where `com.example.app` is the app's reverse domain and `://openid-vp` is the app path that can handle the request)
* HTTP link: `https://example.com/openid-vp` (where `example.com` is a domain registered to open the specific app and configured with proper verification files)
The verifier's website can also include a UI element to allow users to select the app they want to use
for presenting credentials. Based on this selection, the Verifier Web SDK will identify the
application and send the corresponding identifier to the MATTR VII verifier tenant. The tenant will
then return the associated URI (Authorization endpoint).
Regardless of which URI scheme approach you choose, holder apps you expect to verify credentials from must be properly registered with the operating system to handle these schemes. See the following resources for platform-specific implementation guides:
* [Apple Docs - Defining a Custom URL Scheme for Your App](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app)
* [Android Docs - Create Deep Links to App Content](https://developer.android.com/training/app-links/deep-linking)
### The link is used to invoke a compliant application [#the-link-is-used-to-invoke-a-compliant-application]
The link is used by the Verifier SDK to redirect the user to a matching wallet application installed
on the same mobile device. When using a Custom URI a dialogue box appears on the mobile device,
prompting them to launch a matching application (this dialogue box is not displayed for HTTP links).
### The wallet application makes a request to retrieve a request object [#the-wallet-application-makes-a-request-to-retrieve-a-request-object]
The invoked wallet application can now use the link to retrieve the request object from the MATTR
VII verifier tenant. This is a presentation request that defines what information is requested by
the verifier and for what purpose.
In [OAuth 2.0 terminology](https://datatracker.ietf.org/doc/html/rfc6749#section-1.1) this is
referred to as an Authorization request, made by the MATTR VII verifier tenant which acts as a
**Client**. The request is sent to the wallet application which acts as an **Authorization server**,
as it controls access to protected resources, in this case stored credentials.
### The MATTR VII tenant returns the request object [#the-mattr-vii-tenant-returns-the-request-object]
The MATTR VII verifier tenant generates the request object, signs it with its verifier certificate
and sends it as a response to the wallet application's request.
The wallet application will handle the received request object and perform the following:
* Establish trust with the verifier in one of the following methods:
* **Certificate trust model**: The wallet application compares the certificate used to sign the
request object against its list of trusted verifiers. This would be the
[`certificatePem`](/docs/verification/certificates/api-reference/verifier-root-ca-certificates#create-a-verifier-root-ca-certificate)
element of the Verifier root CA, and must be provided by the verifier to the holder out of
band.
* **Domain trust model**: The wallet application retrieves the verifier metadata by making a
request to the `/.well-known/oauth-client` endpoint of the domain the request object is coming
from. By default this would be the verifier's
[`tenant_url`](/docs/platform-management/portal#getting-started). When using a
[Custom domain](/docs/platform-management/custom-domain-overview), you must configure a
[redirect](/docs/platform-management/custom-domain-guide#create-redirects-for-required-assets) for that path from your
custom domain to your MATTR VII verifier tenant.
* Gather credentials matching the information provided in the request object.
* Display this information to the holder and ask for consent to sharing it with the relying party.
The MATTR VII verifier tenant uses a [verifier root CA
certificate](/docs/verification/certificates/api-reference/verifier-root-ca-certificates#create-a-verifier-root-ca-certificate)
to identify itself when interacting with wallet applications.
### The wallet application sends an authorization response [#the-wallet-application-sends-an-authorization-response]
Once the user consents to sharing the information, the wallet application generates an authorization
response, which includes a signed verifiable presentation of matching credentials contained in a
`vp_token` parameter. The authorization response is encrypted and sent to the MATTR VII verifier
tenant.
Upon receiving the authorization response, the MATTR VII verifier tenant would decrypt it and
[verify](/docs/verification/remote-overview) the contained verifiable presentation.
This can include checking whether the certificate used to sign the credential matches an existing
issuer in the tenant's trusted issuers list.
### The MATTR VII verifier tenant returns the verification results [#the-mattr-vii-verifier-tenant-returns-the-verification-results]
The MATTR VII tenant responds with a redirect URI which is used by the wallet application to
redirect the user back to continue the verifier application.
This redirect URI can be configured per verifier application, enabling redirecting users to
different targets based on the verifier application they are interacting with.
When the verifier application has a backend, the MATTR VII verifier tenant can be configured to only
send the results to the backend rather than to the frontend directly. This enhances security as
described in the following flow:
1. The backend creates a unique challenge when the session is established.
2. The verifier application passes the unique challenge with the request to start a presentation
session.
3. The verifier application is notified when the MATTR VII tenant had completed verification.
4. The verifier application passes the session ID to the backend.
5. The backend makes a
[request](/docs/verification/remote-verification-api-reference/presentation-sessions#retrieve-a-presentation-session-result)
to MATTR VII to retrieve the verification results for that session.
6. The MATTR VII tenant responds with the verification result and the unique challenge.
7. The backend compares the original and the received challenge to ensure the response can be
trusted.
8. The backend passes the verification results to the verifier application.
Results availability can be configured per verifier application.
### The verifier application surfaces verification results [#the-verifier-application-surfaces-verification-results]
The user journey can now proceed based on the verifier application business logic.
In our example, if the presented mDoc was verified the user can proceed to opening a bank account,
whereas if verification failed they will receive an error message.
# Apple Identity Access CSRs
URL: /docs/verification/remote-verification-api-reference/apple-identity-access-csr
## Create an Apple Identity Access certificate signing request [#create-an-apple-identity-access-certificate-signing-request]
## Retrieve all Apple Identity Access certificate signing requests [#retrieve-all-apple-identity-access-certificate-signing-requests]
## Retrieve an Apple Identity Access certificate signing request [#retrieve-an-apple-identity-access-certificate-signing-request]
## Delete an Apple Identity Access certificate signing request [#delete-an-apple-identity-access-certificate-signing-request]
# Presentation sessions
URL: /docs/verification/remote-verification-api-reference/presentation-sessions
## Retrieve a presentation session result [#retrieve-a-presentation-session-result]
# Trusted issuers
URL: /docs/verification/remote-verification-api-reference/trusted-issuers
## Create a trusted issuer [#create-a-trusted-issuer]
## Retrieve all trusted issuers (protected) [#retrieve-all-trusted-issuers-protected]
## Retrieve all trusted issuers (public) [#retrieve-all-trusted-issuers-public]
## Retrieve a trusted issuer [#retrieve-a-trusted-issuer]
## Delete a trusted issuer [#delete-a-trusted-issuer]
# Verifier applications
URL: /docs/verification/remote-verification-api-reference/verifier-applications
## Create a verifier application [#create-a-verifier-application]
## Retrieve all verifier applications [#retrieve-all-verifier-applications]
## Retrieve a verifier application [#retrieve-a-verifier-application]
## Update a verifier application [#update-a-verifier-application]
## Delete a verifier application [#delete-a-verifier-application]
# Wallet providers
URL: /docs/verification/remote-verification-api-reference/wallet-providers
## Create a wallet provider [#create-a-wallet-provider]
## Retrieve all wallet providers [#retrieve-all-wallet-providers]
## Retrieve a wallet provider [#retrieve-a-wallet-provider]
## Update a wallet provider [#update-a-wallet-provider]
## Delete a wallet provider [#delete-a-wallet-provider]
# mDocs remote web verification journey pattern
URL: /docs/verification/remote-web-verifiers/journey-pattern
This journey pattern is used to verify an mDoc remotely via an
[online verification workflow](/docs/verification/remote-overview), as per
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html).
## Overview [#overview]
* **Issuance channel**: Remote, unsupervised
* **Device/s**: Same-device / Cross-device
* **Formats**: mDocs
* **Information assurance level**: High
* **Identity assurance level**: High
## Journey flow - Same-device [#journey-flow---same-device]
## Architecture - Same-device [#architecture---same-device]
### Interacting with the website [#interacting-with-the-website]
The user accesses a website using a mobile browser on the same device that holds their wallet app.
### Requesting a credential for verification [#requesting-a-credential-for-verification]
On the website, the user begins an interaction that requires them to present a credential.
The MATTR Pi Verifier Web SDK, embedded in the web application, initiates the verification request by sending it to a configured MATTR VII verifier tenant. This request defines:
* The credentials and claims required
* The supported interaction modes (same-device, in this case)
The MATTR VII verifier tenant is configured with:
* The domains it can accept requests from
* The workflows it supports (e.g. same-device and/or cross-device)
* The supported protocols (e.g. Digital Credentials API and/or OID4VP)
* The wallet applications it can interact with
* How to invoke these wallet applications on the same device
Based on this configuration, the MATTR VII verifier tenant identifies that the user is using a mobile browser and returns a universal link or custom URI that can invoke the wallet app.
The Verifier Web SDK uses this link to redirect the user to their wallet application.
### Presenting request details to the user [#presenting-request-details-to-the-user]
Once the wallet is launched, it authenticates the user and retrieves the verification request from the MATTR VII verifier tenant. The user is shown:
* The credentials being requested
* The claims required from those credentials
* Whether the relying party is trusted and authorized by the Digital Trust Service
* Which of the user’s credentials match the request
The user can then review and consent to sharing the information.
### Verifying the credential [#verifying-the-credential]
The MATTR VII verifier tenant verifies the presentation by checking:
* That the credential has not been tampered with
* That it has not been revoked or suspended
* That it has not expired
* That it was issued by a trusted issuer, validated via the configured Digital Trust Service
### Displaying verification results [#displaying-verification-results]
After the verification is complete, the wallet app redirects the user back to their mobile browser, returning them to the original web application using the previously provided redirect URl.
The Verifier Web SDK receives the verification result and renders it in the browser, allowing the user to view the outcome and continue their interaction.
The issuer of the credential is not informed that the presentation has occurred. No data about the verifier, the context of use, or the interaction itself is shared with the issuer. The only interaction with the issuer is a potential call to an online revocation endpoint, if revocation checking is required.
The MATTR VII verifier tenant can also be configured to return the verification result to a secure back-end service instead of the front-end, depending on implementation needs.
## Journey flow - Cross-device [#journey-flow---cross-device]
## Architecture - Cross-device [#architecture---cross-device]
### Interacting with the website [#interacting-with-the-website-1]
The user accesses a website using a web browser on their desktop.
### Requesting a credential for verification [#requesting-a-credential-for-verification-1]
On the website, the user begins an interaction that requires them to present a credential.
The MATTR Pi Verifier Web SDK, embedded in the web application, initiates the verification request by sending it to a configured MATTR VII verifier tenant. This request defines:
* The credentials and claims required
* The supported interaction modes (same-device, in this case)
The MATTR VII verifier tenant is configured with:
* The domains it can accept requests from
* The workflows it supports (e.g. same-device and/or cross-device)
* The supported protocols (e.g. Digital Credentials API and/or OID4VP)
* The wallet applications it can interact with
* How to invoke these wallet applications on the same device
Based on this configuration, the MATTR VII verifier tenant returns a link. The Verifier Web SDK recognizes Samantha is using a desktop browser and renders this link as a QR code on the webpage.
The user scans the QR code using a mobile device that has a compatible digital wallet installed. This action invokes the wallet app with the verification request.
### Presenting request details to the user [#presenting-request-details-to-the-user-1]
Once the wallet is launched, it authenticates the user and retrieves the verification request from the MATTR VII verifier tenant. The user is shown:
* The credentials being requested
* The claims required from those credentials
* Whether the relying party is trusted and authorized by the Digital Trust Service
* Which of the user’s credentials match the request
The user can then review and consent to sharing the information.
### Verifying the credential [#verifying-the-credential-1]
The MATTR VII verifier tenant verifies the presentation by checking:
* That the credential has not been tampered with
* That it has not been revoked or suspended
* That it has not expired
* That it was issued by a trusted issuer, validated via the configured Digital Trust Service
### Displaying verification results [#displaying-verification-results-1]
The MATTR VII verifier tenant shares the verification results with the Verifier Web SDK. These results are displayed to the user in the browser, allowing them to continue their interaction
The issuer of the credential is not informed that the presentation has occurred. No data about the verifier, the context of use, or the interaction itself is shared with the issuer. The only interaction with the issuer is a potential call to an online revocation endpoint, if revocation checking is required.
The MATTR VII verifier tenant can also be configured to return the verification result to a secure back-end service instead of the front-end, depending on implementation needs.
# Web verification quickstart guide
URL: /docs/verification/remote-web-verifiers/quickstart
This quickstart is for evaluating the MATTR Pi Verifier Web SDK. In about 15-20 minutes you will configure a MATTR VII verifier tenant, run a sample web verifier application, and verify an mDoc presented remotely from the MATTR GO Hold example wallet using the remote presentation workflow defined by OID4VP and ISO/IEC 18013-7:2025.
**Estimated time: 15-20 minutes.**
Use this guide as a fast evaluation path to see the Verifier Web SDK working end-to-end. For more detailed instructions and API examples, see the [tutorial](/docs/verification/remote-web-verifiers/tutorial) and reference documentation.
Using the [Verifier Web SDK](/docs/verification/remote-web-verifiers/sdks/overview) to verify an mDoc presented
remotely to a web application:
## User experience [#user-experience]
In this quickstart you will perform both of these flows yourself using a MATTR VII verifier tenant, the Verifier Web SDK sample application, and the MATTR GO Hold example app:
1. The user interacts with your web application on their mobile device browser.
2. The user is asked to present information as part of the interaction.
3. The user is redirected to their wallet application.
4. The user is informed of what information they are about to share and provide their consent.
5. The wallet application authenticates the user and shares the information with the verifier.
6. The user is redirected back to the web application where verification results are displayed,
enabling them to continue with the interaction.
1. The user interacts with your web application on their desktop browser.
2. The user is asked to present information as part of the interaction.
3. The user scans a QR code using a mobile device with an installed wallet application.
4. The wallet application is launched on the mobile device.
5. The user is informed of what information they are about to share and provide their consent.
6. The wallet application authenticates the user and shares the information with the verifier.
7. Verification results are displayed in the user's desktop browser, enabling them to continue with
the interaction.
## Prerequisites [#prerequisites]
* MATTR VII tenant access via the [MATTR Portal](https://portal.mattr.global/). Apply for access [here](/docs/resources/get-started).
* Install the **MATTR GO Hold example app** for [iOS](https://apps.apple.com/app/mattr-wallet/id1518660243) or [Android](https://play.google.com/store/apps/details?id=global.mattr.wallet).
* Sign up for a free [ngrok.com](https://ngrok.com/) account and make note of your ngrok authentication token. We will be using ngrok to expose your local web application to the internet.
## Part 1: Configure the MATTR VII verifier tenant (5 minutes) [#part-1-configure-the-mattr-vii-verifier-tenant-5-minutes]
These steps configure your verifier tenant so the sample web verifier application can request and verify mDocs issued by a trusted issuer.
### Configure a Verifier application on MATTR VII [#configure-a-verifier-application-on-mattr-vii]
This creates the web verifier application that the Verifier Web SDK will use when interacting with the MATTR VII tenant to request and receive credential presentations.
1. Log into the [MATTR Portal](https://portal.mattr.global/).
2. Switch to your tenant if you have access to multiple tenants, or [create a new tenant](/docs/platform-management/portal#creating-a-tenant) if needed.
3. Expand the **Credential Verification** section in the left-hand navigation panel.
4. Select **Applications**.
5. Select the **Create new** button.
6. Use the *Name* text box to insert a name for your application (e.g. `My Web Verifier`).
7. Use the *Type* radio button to select **Web**.
8. Use the *Allowed domains* text box to insert a placeholder domain name (e.g. `place-holder.com`).\
You will update this placeholder value later.
9. Use the *Redirect URIs* text box to insert a placeholder redirect URI (e.g. `https://place-holder.com/redirect`).\
You will update this placeholder value later.
10. Select the **Create** button to create the new application and display the *Application detail* screen.
11. Copy and record the `ID` value. You will use it in the next step.
### Configure a trusted issuer [#configure-a-trusted-issuer]
In this quickstart you will add a MATTR-provided demo issuer certificate so your verifier accepts mDocs issued by this issuer.
1. Select **Trusted issuers** under the **Credential Verification** section.
2. Select the **Create new** button.
3. Copy and paste the following certificate into the dialogue box.
```
-----BEGIN CERTIFICATE-----
MIICYzCCAgmgAwIBAgIKXhjLoCkLWBxREDAKBggqhkjOPQQDAjA4MQswCQYDVQQG
EwJBVTEpMCcGA1UEAwwgbW9udGNsaWZmLWRtdi5tYXR0cmxhYnMuY29tIElBQ0Ew
HhcNMjQwMTE4MjMxNDE4WhcNMzQwMTE1MjMxNDE4WjA4MQswCQYDVQQGEwJBVTEp
MCcGA1UEAwwgbW9udGNsaWZmLWRtdi5tYXR0cmxhYnMuY29tIElBQ0EwWTATBgcq
hkjOPQIBBggqhkjOPQMBBwNCAASBnqobOh8baMW7mpSZaQMawj6wgM5e5nPd6HXp
dB8eUVPlCMKribQ7XiiLU96rib/yQLH2k1CUeZmEjxoEi42xo4H6MIH3MBIGA1Ud
EwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRFZwEOI9yq
232NG+OzNQzFKa/LxDAuBgNVHRIEJzAlhiNodHRwczovL21vbnRjbGlmZi1kbXYu
bWF0dHJsYWJzLmNvbTCBgQYDVR0fBHoweDB2oHSgcoZwaHR0cHM6Ly9tb250Y2xp
ZmYtZG12LnZpaS5hdTAxLm1hdHRyLmdsb2JhbC92Mi9jcmVkZW50aWFscy9tb2Jp
bGUvaWFjYXMvMjk0YmExYmMtOTFhMS00MjJmLThhMTctY2IwODU0NWY0ODYwL2Ny
bDAKBggqhkjOPQQDAgNIADBFAiAlZYQP95lGzVJfCykhcpCzpQ2LWE/AbjTGkcGI
SNsu7gIhAJfP54a2hXz4YiQN4qJERlORjyL1Ru9M0/dtQppohFm6
-----END CERTIFICATE-----
```
4. Click the **Add** button.
## Part 2: Run the Sample Web Verifier Application (5-10 minutes) [#part-2-run-the-sample-web-verifier-application-5-10-minutes]
In this section you will clone, configure, and run a sample web verifier application that uses the Verifier Web SDK.
### Clone the sample application repository [#clone-the-sample-application-repository]
```bash title="Clone the Sample Apps repository"
git clone https://github.com/mattrglobal/sample-apps.git
```
### Configure the sample application [#configure-the-sample-application]
Configure the sample web verifier with details required to communicate with your MATTR VII tenant and verifier application.
1. Navigate to the sample web verifier application directory:
```bash title="Navigate to the sample web verifier application directory"
cd sample-apps/verifier-web-tutorial
```
2. Rename the `env-template` file to `.env`:
```bash title="Rename the env-template file"
mv env-template .env
```
3. Open the `.env` file in your code editor and only update the following values:
* `NEXT_PUBLIC_TENANT_URL`: Your MATTR VII tenant URL, available in the MATTR Portal under **Platform Management > Tenant**.
* `NEXT_PUBLIC_APPLICATION_ID`: Unique identifier of the Verifier application you created in the previous step.
* `NGROK_AUTHTOKEN`: Your ngrok authentication token.
This is how the values in the updated `.env` file should look like:
```
NEXT_PUBLIC_TENANT_URL="https://learn.vii.au01.mattr.global"
NEXT_PUBLIC_APPLICATION_ID="84a9c4e0-e597-4183-b231-3d0d699104a6"
NGROK_AUTHTOKEN="2bKVt************************************"
```
### Start the application [#start-the-application]
1. Start the application:
```bash title="Start the application"
npm install
npm run dev
```
2. Open your terminal and copy the public URL provided by ngrok (e.g. `https://abc-123-xyz.ngrok-free.app`).
You should now see the sample web verifier application running and reachable at your ngrok public URL.
### Update the Verifier application configuration [#update-the-verifier-application-configuration]
Now point your verifier application configuration at the public URL of the sample web verifier application so the Verifier Web SDK can complete the remote presentation flow.
1. Go back to the MATTR Portal.
2. Expand the **Credential Verification** section in the left-hand navigation panel.
3. Select **Applications**.
4. Select the Verifier application you created in the first step.
5. Select the **Edit** button.
6. Replace the placeholder domain in the *Allowed domains* text box with the ngrok domain from the previous step. Make sure to remove the `https://` prefix (e.g. `abc-123-xyz.ngrok-free.app`).
7. Replace the placeholder redirect URI in the *Redirect URIs* text box with a URI on the same ngrok domain. Make sure to include the `https://` prefix (e.g. `https://abc-123-xyz.ngrok-free.app`).
8. Select the **Update** button.
## Part 3: Test the application (5 minutes) [#part-3-test-the-application-5-minutes]
### Claim a credential to present [#claim-a-credential-to-present]
In order to test the verification flow, you need to have a compatible mDoc in your wallet to present. You can use the MATTR GO Hold example app to claim a demo mDL credential issued by the MATTR demo issuer you added as a trusted issuer in the tenant configuration steps.
1. Open the MATTR GO Hold example app and tap the Blue **Share** button.
2. Select **Respond or Collect** (You may need to allow the app to access your camera).
3. Scan the following QR code:
4. Follow the on-screen instructions to claim the credential into your MATTR GO Hold example app.
This credential will be used in the next step when you test the remote verification flow in the sample web verifier application.
### Test the remote verification flow [#test-the-remote-verification-flow]
Next, use the GO Hold app and the sample web verifier to complete both same-device and cross-device remote verification flows.
1. Open the **ngrok public URL** in a browser on a mobile device where the MATTR GO Hold example app is installed.
2. Select the **Request Credential** button.
3. Select **Allow** to open the MATTR GO Hold example app.
4. The app should launch and display what information is requested for verification.
5. Select **Start**.
6. Select **Confirm** to share the credential.
7. The web application should display the verification results.
1. In a desktop browser, open the **ngrok public URL** (Do not use localhost as the cross-device flow requires a publicly accessible URL).
2. Select the **Request Credential** button. A QR code will be displayed.
3. Open the MATTR GO Hold example app and tap the Blue **Share** button.
4. Select **Respond or Collect** (You may need to allow the app to access your camera).
5. Scan the QR code.
6. The app will display what information is requested for verification.
7. Select **Start**.
8. Select **Confirm**.
9. Back on your desktop browser, the web application should display the verification results.
## Understand the codebase [#understand-the-codebase]
The sample web verifier application uses the [Verifier Web SDK](/docs/verification/remote-web-verifiers/sdks/overview) to implement the OID4VP flow and verify mDocs presented remotely from a wallet. Once you have the sample running, use this section to inspect the key SDK calls you will reuse in your own application.
The main steps are:
### Initialize the SDK [#initialize-the-sdk]
In the sample application, this runs during startup so the Verifier Web SDK knows which tenant and verifier application to use:
```javascript title="Initialize the SDK"
MATTRVerifierSDK.initialize({
apiBaseUrl: "",
applicationId: "",
});
```
* `apiBaseUrl`: The base URL of the MATTR tenant used to handle mDocs verification.
* `applicationId`: The ID of a verifier application in the referenced MATTR tenant.
### Create a credential query [#create-a-credential-query]
Defining a credential query object specifies the type of credential and the attributes you want to request from the user for verification. For example, the following query requests an mDL with specific attributes:
```javascript title="Create a credential request"
const credentialQuery = {
profile:
"MATTRVerifierSDK.OpenidPresentationCredentialProfileSupported.MOBILE",
docType: "org.iso.18013.5.1.mDL",
nameSpaces: {
"org.iso.18013.5.1": {
age_over_18: {},
given_name: {},
family_name: {},
portrait: {},
},
},
};
```
* `profile` : The profile to use for the credential query.
* `docType`: The type of the mDoc to query.
* `nameSpaces`: The namespaces and attributes to request from the mDoc.
### Define credential request options [#define-credential-request-options]
Defining the credential request options specifies the details of how you want to handle the OID4VP flow, including the credential query, challenge and OID4VP configuration:
```javascript title="Define credential request options"
const options = {
credentialQuery: [credentialQuery],
challenge: generateChallenge(),
openid4vpConfiguration: {
redirectUri: "",
walletProviderId: "",
},
};
```
* `credentialQuery` : Define what information is requested from the user for verification.
* `challenge`: A unique challenge string to prevent replay attacks.
* `openid4vpConfiguration`: Configuration for the OID4VP flow:
* `redirectUri`: The web application to redirect the user to after completing a same-device
presentation flow. Must match the redirect URI configured in the Verifier application settings on the MATTR tenant.
* `walletProviderId`: The ID of a trusted wallet provider configured in the Verifier application settings on the MATTR tenant. The
tenant will only accept mDocs presented by wallets from this provider. Refer to the tutorial to learn how to configure a wallet provider ID for a specific wallet application.
### Request credentials and handle the verification results [#request-credentials-and-handle-the-verification-results]
Requesting credentials from the wallet will trigger the OID4VP flow, including a redirection to the wallet for same-device flows or QR code generation for cross-device flows. Once the flow is completed, the results of the verification will be returned to the application:
```javascript title="Request credentials"
const results = await MATTRVerifierSDK.requestCredentials(options);
```
Once results are returned, you can parse and display them to the user as per your requirements.
This quickstart guide is a basic example of how to use the Verifier Web SDK. To make the solution more secure and representative of real-world integrations, it’s recommended to introduce a backend into the workflow. This “back-channel” approach allows your backend to retrieve and validate verification results directly from your MATTR VII tenant. It also enables you to correlate verification outcomes with user sessions in your own system—something that’s essential for realistic POCs and production deployments. Refer to the [tutorial](/docs/verification/remote-web-verifiers/tutorial) for a more complete example of this approach.
## Next steps [#next-steps]
* For your evaluation:
* Note how long this quickstart took and any friction you encountered.
* Consider how the sample query and result handling would map to your real verification use case (for example, which attributes you’d request and how you’d display them).
* Explore the [tutorial](/docs/verification/remote-web-verifiers/tutorial) for detailed instructions and explanations.
* Refer to the [DC API](/docs/verification/remote-web-verifiers/dc-api/guide) guide to learn how to implement the same verification flow using the Digital Credentials API to request credentials.
# Learn how to build and configure a web application that can verify mDocs
URL: /docs/verification/remote-web-verifiers/tutorial
## Introduction [#introduction]
In this tutorial, you will use the [MATTR Portal](/docs/platform-management/portal) and the
[Verifier Web SDK](/docs/verification/remote-web-verifiers/sdks/overview) to build and configure a web
application that can verify an mDoc presented via an
[online presentation workflow](/docs/verification/remote-overview) as per
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html).
This web application will support both same-device and cross-device workflows to accommodate flexible user journeys.
1. The user interacts with your web application on their mobile device browser.
2. The user is asked to present information as part of the interaction.
3. The user is redirected to their wallet application.
4. The user is informed of what information they are about to share and provide their consent.
5. The wallet application authenticates the user and shares the information with the verifier.
6. The user is redirected back to the web application where verification results are displayed,
enabling them to continue with the interaction.
The result will look something like this:
1. The user interacts with your web application on their desktop browser.
2. The user is asked to present information as part of the interaction.
3. The user scans a QR code using a mobile device with an installed wallet application.
4. The wallet application is launched on the mobile device.
5. The user is informed of what information they are about to share and provide their consent.
6. The wallet application authenticates the user and shares the information with the verifier.
7. Verification results are displayed in the user's desktop browser, enabling them to continue with
the interaction.
The result will look something like this:
## Prerequisites [#prerequisites]
Before you get started, let's make sure you have everything you need.
### Prior knowledge [#prior-knowledge]
* We recommend you make yourself familiar with the following concepts to support your understanding
of the concepts included in this tutorial:
* What is [Credential verification](/docs/verification)?
* What are [mDocs](/docs/concepts/mdocs)?
* What steps does an
[online verification workflow](/docs/verification/remote-web-verifiers/workflow) comprise?
### Assets [#assets]
* Complete the [sign up form](/docs/resources/get-started) to get trial access to MATTR VII and
the MATTR Portal.
* Use the MATTR Portal to [create a new tenant](/docs/platform-management/portal#getting-started).
* Install the **MATTR GO Hold example app** by following the
[getting started guide](/docs/holding/go-hold/getting-started) and use it to
[claim an mDoc](/docs/holding/go-hold/getting-started#claim-a-credential).
* Refer to the completed implementation in our [Sample Apps repository](https://github.com/mattrglobal/sample-apps/tree/master/verifier-web-tutorial) to compare with your progress at any time.
### Development environment [#development-environment]
* Download and install [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).
* To test locally you will need to expose your web application to the internet. You can do this by setting up a [free ngrok account](https://ngrok.com/), using a cloudflare tunnel or using your own solution. For this tutorial we will be using ngrok. Sign up for a free account at [ngrok.com](https://ngrok.com/).
* You will need a code editor such as [VS Code](https://code.visualstudio.com/download).
Got everything? Let's get going!
## Overview [#overview]
The following diagram depicts the workflow you will build in this tutorial:
1. The user triggers the workflow by interacting with the web application.
2. The customer's backend creates a unique challenge and passes it to the customer's web application.
3. The web application passes the unique challenge with the request to start a presentation session.
4. MATTR VII interacts with the user's wallet application to complete the verification workflow.
5. The web application is notified when the MATTR VII tenant has completed verification.
6. The web application passes the session ID to the backend.
7. The backend makes a [request](/docs/verification/remote-verification-api-reference/presentation-sessions#retrieve-a-presentation-session-result) to MATTR VII to retrieve the verification results for that session.
8. The MATTR VII tenant responds with the verification result and the unique challenge.
9. The backend compares the original and the received challenge to ensure the response can be
trusted.
10. The backend passes the verification results to the web application.
You will build this workflow in three parts:
1. [Part 1: Use the MATTR Portal to configure the MATTR VII verifier tenant](#part-1-configure-the-mattr-vii-verifier-tenant).
2. [Part 2: Build a web application with mDocs verification capabilities](#part-2-build-a-web-application-with-mdocs-verification-capabilities).
3. [Part 3: Integrate a backend](#part-3-integrate-a-backend-into-your-online-verification-workflow).
## Part 1: Configure the MATTR VII verifier tenant [#part-1-configure-the-mattr-vii-verifier-tenant]
The MATTR VII verifier tenant will be used to interact with your web application (generating a
verification request) and the wallet application (presenting an mDoc for verification) as per OID4VP
and ISO/IEC 18013-7. To enable this, you must:
1. [Create a verifier application configuration](#create-a-verifier-application-configuration):
Define what applications can create verification sessions with the MATTR VII tenant, and how to
handle these requests.
2. [Create a supported wallet configuration](#create-a-supported-wallet-configuration): Define how
to invoke specific wallet applications as part of an online verification workflow.
3. [Configure a trusted issuer](#configure-a-trusted-issuer): The MATTR VII verifier tenant will
only accept mDocs issued by these trusted issuers.
You can perform these steps via the Portal or by making API requests to your MATTR VII tenant.
### Create a verifier application configuration [#create-a-verifier-application-configuration]
Each MATTR VII tenant can interact with multiple verifier applications, and can handle requests
differently for each application. This means you must create a verifier application configuration
that defines how to handle verification requests from your web application.
1. Expand the **Credential Verification** section in the left-hand navigation panel.
2. Select **Applications**.
3. Select the **Create new** button.
4. Use the *Name* text box to insert a meaningful and friendly name for your application.
5. Use the *Type* radio button to select **Web**.
6. Use the *Allowed domains* text box to insert a placeholder domain name (e.g. `place-holder.com`).
This indicates domain names that the MATTR VII tenant can verify incoming requests are from known
and trusted applications. You will update this placeholder value later.
7. Use the *Redirect URIs* text box to insert a placeholder redirect URI (e.g.
`https://place-holder.com`). This is the URI the user would be redirected to after presenting a
credential on a same-device flow. You will update this placeholder value later.
8. Select the **Create** button to create the new application and display the *Application detail*
screen.
9. Copy and record the `ID` value. You will use it later in the tutorial.
Make the following request to your MATTR VII tenant to
[create a verifier application configuration](/docs/verification/remote-verification-api-reference/verifier-applications#create-a-verifier-application):
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "My Verifier Web Application",
"type": "web",
"domain": "place-holder.com",
"openid4vpConfiguration": {
"supportedModes": "all",
"redirectUris": ["https://place-holder.com"],
"display": {
"logoImage": {
"url": "https://static.mattr.global/logos/mattr/s/gFC.svg",
"altText": "MATTR Logo image"
},
"headerText": "Share your information.",
"bodyText": "Please scan the QR code to provide information required for completing this interaction."
}
},
"resultAvailableInFrontChannel": true
}
```
* `name` : You can use whatever name you'd like, as long as it is unique on your tenant.
* `type` : We will use `web` as we are building a web application.
* `domain` : Here we define the web application domain name so that the MATTR VII tenant can verify
incoming requests are from known and trusted applications. As `localhost` is not supported we will
use a tunneling service (ngrok) to test this tutorial. We will update this placeholder value
later.
* `openid4vpConfiguration`:
* `supportedModes` : Setting this to `all` indicates that your web application will support both
same-device and cross-device workflows.
* `redirectUris` : This is the URI the user would be redirected to after presenting a credential
on a same-device flow. We will update this placeholder value later.
* `display` : Adjust the iframe modal appearance in cross-device flows:
* `logoImage` :
* `url` : Insert any publicly available URL of a logo image which is displayed on the
top left corner of the iframe modal.
* `altText` : Edit the logo image alternative text.
* `headerText` : Edit the header displayed in the iframe modal.
* `bodyText` : Edit the text displayed in the iframe modal.
* `resultAvailableInFrontChannel` : Setting this to `true` makes the verification results available
directly to the web application. You can change this setting later if you choose to
[integrate a backend](#part-3-integrate-a-backend-into-your-remote-verification-workflow) into the
workflow.
*Response*
```json title="Response body"
{
"id": "0eaa8074-8cc4-41ec-9e42-072d36e2acb0", // [!code focus]
"name": "My Verifier Web Application"
//... rest of application configuration
}
```
* `id` : We will use this value later to initialize the SDK so that requests coming from your web
application can be recognized and trusted by the MATTR VII tenant.
### Create a supported wallet configuration [#create-a-supported-wallet-configuration]
Verifier web applications can define specific wallet applications to accept mDocs from as part of
their verification workflows. The MATTR VII verifier tenant needs to be configured with a specific
URI scheme that will be used to invoke these wallets.
1. Expand the **Credential Verification** section in the left-hand navigation panel.
2. Select **Supported wallets**.
3. Select the **Create new** button.
4. Use the *Name* text box to insert a meaningful and friendly name for your wallet application (for
example "MATTR GO Hold").
5. Use the *Authorize endpoint* text box to insert `mdoc-openid4vp://`. This is the URI scheme that
will be used to invoke the wallet application. More information on applying different URI schemes and the resulting user experience can be found in the [workflow page](/docs/verification/remote-web-verifiers/workflow#the-mattr-vii-verifier-tenant-responds-with-a-link-to-invoke-a-matching-application).
6. Select the **Create** button to create the new wallet configuration.
The authorization endpoint configured in the example above
(`mdoc-openid4vp://`) is the default OID4VP scheme. While this is technically
redundant, we chose to include this step to explain how to adjust this
configuration for wallet applications using different schemes. More information on applying different URI schemes and the resulting user experience can be found in the [workflow page](/docs/verification/remote-web-verifiers/workflow#the-mattr-vii-verifier-tenant-responds-with-a-link-to-invoke-a-matching-application).
Make the following request to your MATTR VII tenant to
[create a trusted wallet provider configuration](/docs/verification/remote-verification-api-reference/wallet-providers#create-a-wallet-provider):
```http title="Request"
POST /v2/presentations/wallet-providers
```
```json title="Request body"
{
"name": "MATTR GO Hold", // [!code focus]
"openid4vpConfiguration": {
"authorizationEndpoint": "mdoc-openid4vp://" // [!code focus]
}
}
```
* `name` : Unique name to identify this trusted wallet provider.
* `authorizationEndpoint` : URI scheme that will be used to invoke the wallet application. More information on applying different URI schemes and the resulting user experience can be found in the [workflow page](/docs/verification/remote-web-verifiers/workflow#the-mattr-vii-verifier-tenant-responds-with-a-link-to-invoke-a-matching-application).
The `authorizationEndpoint` configured in the example above
(`mdoc-openid4vp://`) is the default OID4VP scheme. While this is technically
redundant, we chose to include this step to explain how to configure this
endpoint for wallet application using different schemes. More information on applying different URI schemes and the resulting user experience can be found in the [workflow page](/docs/verification/remote-web-verifiers/workflow#the-mattr-vii-verifier-tenant-responds-with-a-link-to-invoke-a-matching-application).
*Response*
```json title="Response body"
{
"id": "99890c34-e4b7-4a23-84d6-e5de57114c00", // [!code focus]
"name": "MATTR GO Hold",
"openid4vpConfiguration": {
"authorizationEndpoint": "mdoc-openid4vp://"
}
}
```
* `id` : We will use this value later to indicate this is the wallet the web application expects to
receive mDocs from.
### Configure a trusted issuer [#configure-a-trusted-issuer]
You must configure trusted issuers on your MATTR VII verifier tenant, as presented mDocs will only
be verified if they had been issued by a trusted issuer.
This is achieved by providing the PEM certificate of the IACA used by these issuers to sign mDocs. In production environments this certificate can be retrieved in one of two ways:
* The issuer can provide it directly to the verifier out of band.
* The verifier can retrieve it from the issuer's metadata:
1. Make a GET request to the issuer's `/.well-known/openid-credential-issuer` endpoint.
2. Retrieve the `mdoc_iacas_uri` element from the response.
3. Make a GET request to that URI to retrieve the IACAs list (for example for a MATTR VII Issuer
tenant this URI would be `{tenant_url}/v1/openid/iacas`).
1. Expand the **Credential Verification** section in the left-hand navigation panel.
2. Select **Trusted issuers**.
3. Select the **Create new** button.
4. Use the *Certificate PEM file* to upload the
[following file](http://files.mattr.global/learn/montcliff-iaca.pem.zip) (ensure you extract the
ZIP file and upload the .pem file itself). This is the IACA certificate that identifies the
[MATTR Labs demo issuer](https://montcliff-dmv.mattrlabs.com/dashboard) which issues the
credential referenced in the [prerequisites](#assets) section.
5. Select the **Add** button to add the new trusted issuer.
This completes the configuration of the MATTR VII verifier tenant. In the next step, you will build
a simple application that you can run locally to test the verification workflow and ensure
everything is functioning as expected.
Make the following request to your MATTR VII tenant to
[configure a truster issuer](/docs/verification/remote-verification-api-reference/trusted-issuers#create-a-trusted-issuer):
```http title="Request"
POST /v2/credentials/mobile/trusted-issuers
```
```json title="Request body"
{
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIICYzCCAgmgAwIBAgIKXhjLoCkLWBxREDAKBggqhkjOPQQDAjA4MQswCQYDVQQG\nEwJBVTEpMCcGA1UEAwwgbW9udGNsaWZmLWRtdi5tYXR0cmxhYnMuY29tIElBQ0Ew\nHhcNMjQwMTE4MjMxNDE4WhcNMzQwMTE1MjMxNDE4WjA4MQswCQYDVQQGEwJBVTEp\nMCcGA1UEAwwgbW9udGNsaWZmLWRtdi5tYXR0cmxhYnMuY29tIElBQ0EwWTATBgcq\nhkjOPQIBBggqhkjOPQMBBwNCAASBnqobOh8baMW7mpSZaQMawj6wgM5e5nPd6HXp\ndB8eUVPlCMKribQ7XiiLU96rib/yQLH2k1CUeZmEjxoEi42xo4H6MIH3MBIGA1Ud\nEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRFZwEOI9yq\n232NG+OzNQzFKa/LxDAuBgNVHRIEJzAlhiNodHRwczovL21vbnRjbGlmZi1kbXYu\nbWF0dHJsYWJzLmNvbTCBgQYDVR0fBHoweDB2oHSgcoZwaHR0cHM6Ly9tb250Y2xp\nZmYtZG12LnZpaS5hdTAxLm1hdHRyLmdsb2JhbC92Mi9jcmVkZW50aWFscy9tb2Jp\nbGUvaWFjYXMvMjk0YmExYmMtOTFhMS00MjJmLThhMTctY2IwODU0NWY0ODYwL2Ny\nbDAKBggqhkjOPQQDAgNIADBFAiAlZYQP95lGzVJfCykhcpCzpQ2LWE/AbjTGkcGI\nSNsu7gIhAJfP54a2hXz4YiQN4qJERlORjyL1Ru9M0/dtQppohFm6\n-----END CERTIFICATE-----"
}
```
* `certificatePem` : This is the IACA identifying the
[MATTR Labs demo issuer](https://montcliff-dmv.mattrlabs.com/dashboard) which issues the
credential referenced in the [Testing device prerequisite](#testing-device).
If you intend to test this tutorial with a credential different than the one
recommended in our [Testing device prerequisite](#testing-device), replace the
`certificatePem` value with your own issuer's IACA.
*Response*
A successful `201` response indicates that this issuer's certificate was added to your MATTR VII
tenant's trusted issuer's list. This means that mDocs that use this IACA as their root certificate
can be trusted and verified.
## Part 2: Build a web application with mDocs verification capabilities [#part-2-build-a-web-application-with-mdocs-verification-capabilities]
In this part of the tutorial, the verification result is passed directly from your MATTR VII tenant to your front-end application. This “front-channel” approach is designed for simplicity—it’s ideal for quick proofs of concept (POCs), experiments, or early-stage integrations where you want to see results quickly without needing a backend in place.
However, for more realistic POCs and any production-ready implementation, you’ll want to use a “back-channel” approach. In this model, your backend retrieves and verifies the result directly, allowing it to validate the original challenge and securely bind the verification outcome to a user session in your own system. This helps ensure the integrity of the verification flow, prevents tampering or spoofed responses, and allows your backend to maintain full trust in the result.
In the next part of the tutorial, we’ll show you how to extend your implementation with a backend service that securely retrieves verification results and ties them to user sessions in your own environment.
Now that the MATTR VII verifier tenant is properly configured, you can proceed with the steps
required to embed verification capabilities into your web application:
1. [Setup your environment](#setup-your-environment): Setup the required infrastructure for your web
application.
2. [Update the domain and redirect URI](#update-the-domain-and-redirect-uri): Once your tunneling
service is up and running you can update the placeholder values in the MATTR VII verifier
application configuration.
3. [Initialize the SDK](#initialize-sdk): So that the SDK functions are available in your web
application.
4. [Create a credential query](#create-credential-request): Define what information is required for
verification and how to handle the interaction with the user.
5. [Handle verification results](#handle-verification-results): Create the logic that enables your
web application to retrieve verification results in different workflows.
### Setup your environment [#setup-your-environment]
1. Open your terminal and create a new NextJS default project:
```bash title="BASH"
npx create-next-app@latest --src-dir --yes
```
2. Approve installing any required packages.
3. Insert a name for your project (e.g. `my-verifier-web-application`).
4. Open the project app's folder:
```bash title="BASH"
cd my-verifier-web-application
```
5. Install the SDK:
```bash title="BASH"
npm install @mattrglobal/verifier-sdk-web
```
6. Open the `src/app/page.tsx` file in your preferred code editor and replace all existing code with
the following:
```tsx title="tsx"
"use client";
// Step 3.2 - Add dependencies
export default function Home() {
// Step 5.1 - Store results state
// Step 6.1 - Create createRequest function
// Step 6.3 - Create retrieveResults function
// Step 4.1 - Create requestCredentials function
// Step 5.4 - Handle same-device redirect
// Step 3.3 - Initialize the SDK
// Step 5.5 - Add effect to handle redirect
return (
);
}
```
This will serve as the basic structure for your application. We will copy and paste
different code snippets into specific locations to achieve the different functionalities.
These locations are indicated by comments that reference the corresponding tutorial step (e.g. `// Step 3.2 - Add dependencies`).
We recommend copying and pasting the comment text to easily locate it in the code.
7. Run the project:
```bash title="BASH"
npm run dev
```
This will run the app and make it available at [http://localhost:3000](http://localhost:3000) (or the next available port if
3000 is already used).
8. Start an ngrok tunnel in a new terminal window:
```bash title="BASH"
ngrok http http://localhost:3000
```
If your web application is running on a port different than 3000, use that value in the ngrok
command above.
Your terminal window should show the tunnel details:
9. Make note of the `Forwarding` value, we will use it in the next step.
### Update the domain and redirect URI [#update-the-domain-and-redirect-uri]
With the tunneling service now active, update the MATTR VII verifier application configuration to
include the domain and redirect URI details.
1. Expand the **Credential Verification** section in the left-hand navigation panel.
2. Select **Applications**.
3. Select the verifier application configuration you created in the
[previous step](#create-a-verifier-application-configuration).
4. Update the *Allowed domains* text box to include the `Forwarding` URL from the ngrok tunnel,
removing the `https://` prefix.
5. Update the *Redirect URIs* text box to include the `Forwarding` URL from the ngrok tunnel.
6. Select the **Update** button to update the application configuration.
The `Forwarding` URL is a temporary URL that will be used to test the web
application. It will change every time you restart the ngrok tunnel, so you
will need to repeat this step each time you restart the tunnel.
Make the following request to your MATTR VII tenant to update the verifier application configuration with the tunneling service details:
```http title="Request"
PUT /v2/presentations/applications/{applicationId}
```
* `applicationId` : Replace with the `id` from the response returned when you
[created the verifier application configuration](#create-a-verifier-application-configuration).
```json title="Request body"
{
"name": "My Verifier Web Application",
"type": "web",
"domain": "your-ngrok-subdomain.ngrok.io", // [!code focus]
"openid4vpConfiguration": {
"supportedModes": "all",
"redirectUris": ["https://your-ngrok-subdomain.ngrok.io"], // [!code focus]
"display": {
"logoImage": {
"url": "https://static.mattr.global/logos/mattr/s/gFC.svg",
"altText": "MATTR Logo image"
},
"headerText": "Share your information.",
"bodyText": "Please scan the QR code to provide information required for completing this interaction."
}
},
"resultAvailableInFrontChannel": true
}
```
* `domain` : Update this value to include the `Forwarding` URL from the ngrok tunnel,
removing the `https://` prefix.
* `redirectUris` : Update this value to include the `Forwarding` URL from the ngrok tunnel.
### Initialize SDK [#initialize-sdk]
1. Create a new file named `.env.local` in the projects root folder and add the following code to
add your tenant's URL to the project's variables:
```tsx title=".env.local"
NEXT_PUBLIC_TENANT_URL=https://learn.vii.au01.mattr.global
NEXT_PUBLIC_APPLICATION_ID=0eaa8074-8cc4-41ec-9e42-072d36e2acb0
NEXT_PUBLIC_WALLET_PROVIDER_ID=99890c34-e4b7-4a23-84d6-e5de57114c00
```
* `NEXT_PUBLIC_TENANT_URL` : Replace the parameter value with your
[`tenant_url`](/docs/platform-management/portal#getting-started).
* `NEXT_PUBLIC_APPLICATION_ID` : Replace the parameter value with the `id` from the response returned when you [created the verifier application configuration](#create-a-verifier-application-configuration).
* `NEXT_PUBLIC_WALLET_PROVIDER_ID` : Replace the parameter value with the `id` from the response returned when you [created a wallet configuration](#create-a-supported-wallet-configuration).
2. Copy and paste the following code under the `Step 3.2 - Add dependencies` comment to add required
dependencies to your web application:
```tsx title="src/app/page.tsx"
import * as MATTRVerifierSDK from "@mattrglobal/verifier-sdk-web";
import { useCallback, useEffect, useState } from "react";
```
3. Copy and paste the following code under the `Step 3.3 - Initialize the SDK` comment to create a
new function that will be used by your web application to initialize the SDK:
```tsx title="src/app/page.tsx"
useEffect(() => {
MATTRVerifierSDK.initialize({
apiBaseUrl: process.env.NEXT_PUBLIC_TENANT_URL as string,
applicationId: process.env.NEXT_PUBLIC_APPLICATION_ID as string,
});
}, []);
```
The function calls the SDK's
[initialize](https://api-reference-sdk.mattr.global/verifier-sdk-web/latest/functions/initialize.html)
function that makes the SDK methods available in your web application while defining the
following parameters:
* `apiBaseUrl`: This value is retrieved from the `NEXT_PUBLIC_TENANT_URL` project variable
defined above. It indicates to the Verifier Web SDK what MATTR VII tenant to interact with in
order to verify presented mDocs.
* `applicationId` : This value is retrieved from the `NEXT_PUBLIC_APPLICATION_ID` project variable defined above.
This will be used by the MATTR VII tenant to identify verification requests from your web
application.
### Create credential request [#create-credential-request]
Now that we have a function and a UI in place, we need to define what the function does by creating
a credential request. This request will define what information is requested from the user, and how
to interact with the MATTR VII verifier tenant.
1. Copy and paste the following code under the `Step 4.1 - Create requestCredential function`
comment to create a new `requestCredentials` function:
```tsx title="src/app/page.tsx"
const requestCredentials = useCallback(async () => {
const credentialQuery = {
profile:
MATTRVerifierSDK.OpenidPresentationCredentialProfileSupported.MOBILE,
docType: "org.iso.18013.5.1.mDL",
nameSpaces: {
"org.iso.18013.5.1": {
age_over_18: {},
given_name: {},
family_name: {},
portrait: {},
},
},
} as MATTRVerifierSDK.CredentialQuery;
// Step 4.2 - Add credential request
// Step 5.2 - Retrieve cross-device verification
// Step 5.3 - Add setResults
}, []);
```
The function includes a `credentialQuery` variable that defines what information is requested
from the user for verification. This example will request the `age_over_18`, `given_name`,
`family_name` and `portrait` claims from a credential of profile `mobile` and docType
`org.iso.18013.5.1.mDL`, under the `org.iso.18013.5.1` namespace. Presenting a credential with
different claims, profile, docType or namespace will fail verification.
2. Copy and paste the following code under the `Step 4.2 - Add credential request` comment so that the new `requestCredentials` function calls the SDK's
[`requestCredentials`](https://api-reference-sdk.mattr.global/verifier-sdk-web/latest/functions/requestCredentials.html)
function, passing the `credentialQuery` variable as a parameter:
```tsx title="src/app/page.tsx"
const options: MATTRVerifierSDK.RequestCredentialsOptions = {
credentialQuery: [credentialQuery],
// Step 6.2 - Use challenge from backend
challenge: MATTRVerifierSDK.utils.generateChallenge(),
openid4vpConfiguration: {
redirectUri: window.location.origin,
walletProviderId: process.env.NEXT_PUBLIC_WALLET_PROVIDER_ID,
},
};
const results = await MATTRVerifierSDK.requestCredentials(options);
```
* `options` : This object defines the required parameters for calling the SDK's
[`requestCredentials`](https://api-reference-sdk.mattr.global/verifier-sdk-web/latest/functions/requestCredentials.html)
function. It is passed as a parameter to the function when calling it.
* `credentialQuery` : Defines what information is requested from the user for verification. In
our example we are using the credential query created in the previous step.
* `challenge` : Unique challenge generated by the SDK to identify this specific presentation
session. We will use it in
[part 3](#part-3-integrate-a-backend-into-your-remote-verification-workflow) of the tutorial.
* `openid4vpConfiguration`: This object defines the configuration necessary for an OID4VP
presentation flow:
* `redirectUri` : Indicates that the user will be redirected to the same web application
screen when completing a same-device workflow. It can be any other path on your
application domain, as long as it is handled by the application. Must be on the same domain where the interaction started. See
[the workflow documentation](/docs/verification/remote-web-verifiers/workflow#the-mattr-vii-verifier-tenant-returns-the-verification-results) for more information.
* `walletProviderId` : This value is retrieved from the `NEXT_PUBLIC_WALLET_PROVIDER_ID` project variable defined above.
3. Copy and paste the following code under the `Step 4.3 - Add request credentials button` comment
to create a new button that the user will use to interact with the web application:
```tsx title="src/app/page.tsx"
```
Once the user selects this button, the web application will trigger the verification workflow by
calling the `requestCredentials` function created above:
* This function calls the SDK's
[`requestCredentials`](https://api-reference-sdk.mattr.global/verifier-sdk-web/latest/functions/requestCredentials.html)
method which starts a presentation session with the configured MATTR VII session.
* Once a session is established, the MATTR VII verifier tenant will interact with the user's
wallet application to request and verify the details provided in the query.
* When the verification process is complete, the MATTR VII verifier tenant will return the
verification results to your web application.
### Handle verification results [#handle-verification-results]
1. Copy and paste the following code under the `Step 5.1 - Store results state` comment to create a
variable that will store the verification state:
```tsx title="src/app/page.tsx"
const [results, setResults] =
useState(null);
```
2. Copy and paste the following code under the
`Step 5.2 - Retrieve cross-device verification results` comment to retrieve verification results
in cross-device workflows:
```tsx title="src/app/page.tsx"
if (results.isOk()) {
// Step 6.4 - Call retrieveResults function in cross-device workflow
setResults(
results.value.result as MATTRVerifierSDK.PresentationSessionResult,
);
} else {
alert(`Error retrieving results: ${results.error.message}`);
}
```
3. Add `setResults` in the square brackets under the `// Step 5.3 - Add setResults` to set the
`results` state variable once results are available. Your code should look as follows:
```tsx title="src/app/page.tsx"
}, [setResults])
```
4. Copy and paste the following code under the `Step 5.4 - Handle same-device redirect` comment to
create a new `handleRedirect` function:
```tsx title="src/app/page.tsx"
const handleRedirect = useCallback(async () => {
const results = await MATTRVerifierSDK.handleRedirectCallback();
if (results.isOk()) {
// Step 6.6 - Call retrieveResults function on same-device redirect
if (results.value.result) {
setResults(results.value.result);
}
} else {
alert(`Error retrieving results: ${results.error.message}`);
return;
}
// Step 6.7 - Replace setResults function with retrieveResults function
}, [setResults]);
```
This function calls the SDK's
[handleRedirectCallback](https://api-reference-sdk.mattr.global/verifier-sdk-web/latest/functions/handleRedirectCallback.html)
function, which returns the verification results as the user is redirected following a
same-device presentation workflow. These results (`results.value.result`) are then assigned to
the `results` state variable by `setResults`.
5. Copy and paste the following code under the `Step 5.5 - Add effect to handle redirect` comment to
add an effect that calls the `handleRedirect` function when the web application is refreshed with
a hash, indicating a redirect after completing a same-device presentation workflow:
```tsx title="src/app/page.tsx"
useEffect(() => {
if (window.location.hash) {
handleRedirect();
}
}, [handleRedirect]);
```
6. Copy and paste the following code under the `Step 5.6 - Render results` comment to retrieve
properties of the `results` object once it is available, and display them to the user as part of
the interaction:
```tsx title="src/app/page.tsx"
Results
{
!results && "No results available"
}
{
results && "credentials" in results && (
)
}
```
In this example when verification is successful (`"credentials" in results`) the app retrieves
and displays the following properties of the first credential (`credentials?.[0]`) of the
`results` object:
* `verificationResult.verified` : Indicates whether or not the mDoc was verified.
* `validityInfo.validUntil` : Indicates the mDoc expiry date.
* `issuerInfo.commonName` : Indicates the issuer's name.
* `claims?.['org.iso.18013.5.1'].given_name` : Indicates the holder's given name.
* `claims?.['org.iso.18013.5.1'].age_over_18.value` : Indicates whether or not the holder is
over 18.
When verification fails (`"error" in results`) the app retrieves and displays error details
(`error.type` and `error.message`) from the `results` response.
Your web application will now continue the interaction based on the workflow type:
* For cross-device workflows, the web application will display the verification results in the
desktop browser where the interaction started. The user can then continue the interaction on this
desktop browser.
* For same-device workflows, the MATTR VII verifier tenant will redirect the user back to the web
application in their mobile browser, where verification will be displayed. The user can then
continue the interaction on this mobile browser.
Congratulations, your web application is now ready to verify mDocs.
Let's test what we've built!
### Test the front channel interaction [#test-the-front-channel-interaction]
1. Open your **ngrok forwarding URL** in a browser on a mobile device where the
[MATTR GO Hold example app](/docs/holding/go-hold/getting-started) is installed.
2. Select the **Request Credential** button.
3. Select **Allow** to open the GO Hold app.
4. The app should launch and display what information is requested for verification.
5. Select **Start**.
6. Select **Confirm** to share a credential which includes the requested information (claim one
[here](/docs/holding/go-hold/getting-started#claim-a-credential) if you haven't done so
already).
7. The web application should display the verification results as a string.
The result should look something like this:
1. In a desktop browser, open your public **ngrok forwarding URL** (Do not use localhost as the cross-device flow requires a publicly accessible URL).
2. Select the **Request Credential** button. A QR code will be displayed.
3. Open the camera on a mobile device where MATTR GO Hold is installed and scan the QR code.
4. Confirm opening the QR code with the GO Hold app.
5. The app should launch and display what information is requested for verification.
6. Select **Start**.
7. Select **Confirm** to share a credential which includes the requested information (claim one
[here](/docs/holding/go-hold/getting-started#claim-a-credential) if you haven't done so
already).
8. Back on your desktop browser you the web application should display the verification results as a
string.
The result should look something like this:
If your goal was just to see mDocs online verification in action, there's no
need to proceed to the next step of the tutorial. However, production
solutions often integrate a backend, and if you're curious about how that
works, feel free to continue to the next step—it's optional and won't change
the overall workflow, just what’s happening behind the scenes.
## Part 3: Integrate a backend into your online verification workflow [#part-3-integrate-a-backend-into-your-online-verification-workflow]
Your online verification workflow is now working well using a front-channel flow, where results are passed directly from the MATTR VII tenant to your front-end application. This approach is ideal for quick and easy POCs, experiments, or demonstrations where you want to get up and running fast.
To make your solution more secure and representative of real-world integrations, it’s recommended to introduce a backend into the workflow. This “back-channel” approach allows your backend to retrieve and validate verification results directly from your MATTR VII tenant. It also enables you to correlate verification outcomes with user sessions in your own system—something that’s essential for realistic POCs and production deployments.
In this part of the tutorial, you’ll configure a simple backend service that securely retrieves verification results from your MATTR VII tenant, validates them, and then passes the verified outcome to your front-end application.
For more details on how the end-to-end workflow operates, refer to the [detailed workflow documentation](/docs/verification/remote-web-verifiers/workflow#the-mattr-vii-verifier-tenant-returns-the-verification-results).
### Update verifier application configuration [#update-verifier-application-configuration]
The first thing you need to do is to make sure verification results are not returned directly to the
web application's front channel. This setting is part of your MATTR VII verifier application
configuration.
1. Expand the **Credential Verification** section in the left-hand navigation panel.
2. Select **Applications**.
3. Select the verifier application configuration you created in the
[previous step](#create-a-verifier-application-configuration).
4. Use the *Verification results return method* radio button to select **Must be retrieved and
validated by backend**.
5. Select the **Update** button to update the application configuration.
This setting is only available when the application type is set to **Web**.
Make the following request to your MATTR VII tenant to update the verifier application configuration so that verification results are not returned directly to the web application's front channel:
```http title="Request"
PUT /v2/presentations/applications/{applicationId}
```
* `applicationId` : Replace with the `id` from the response returned when you
[created the verifier application configuration](#create-a-verifier-application-configuration).
```json title="Request body"
{
"name": "My Verifier Web Application",
"type": "web",
"domain": "your-ngrok-subdomain.ngrok.io",
"openid4vpConfiguration": {
"supportedModes": "all",
"redirectUris": ["https://your-ngrok-subdomain.ngrok.io"],
"display": {
"logoImage": {
"url": "https://static.mattr.global/logos/mattr/s/gFC.svg",
"altText": "MATTR Logo image"
},
"headerText": "Share your information.",
"bodyText": "Please scan the QR code to provide information required for completing this interaction."
}
},
"resultAvailableInFrontChannel": false // [!code focus]
}
```
* `resultAvailableInFrontChannel` : Set this value to `false` to ensure that
verification results are not returned directly to the web application's front channel. This is the only change required in the request body.
### Add your MATTR VII login details [#add-your-mattr-vii-login-details]
Your backend will need to make a request to a protected MATTR VII endpoint to retrieve the
verification results. To enable this, we must provide it with your
[MATTR VII tenant login details](/docs/platform-management/portal#getting-started).
1. Add the following code to your `.env.local` file to add your
[MATTR VII tenant login details](/docs/platform-management/portal#getting-started):
```tsx title=".env.local"
MATTR_CLIENT_ID=ZAGvD********************************
MATTR_CLIENT_SECRET=uc8NM****************************************
```
* `MATTR_CLIENT_ID` : Replace with your `client_id` value.
* `MATTR_CLIENT_SECRET` : Replace with your `client_secret` value.
### Create a session and associate a challenge [#create-a-session-and-associate-a-challenge]
The web application backend needs to generate a unique challenge in the context of an existing user
session. In a typical application, the user would likely already interact with the application
inside an active session, and often that session is tracked using a cookie. In this tutorial we are
mimicking this by setting a random value as the user session ID.
We are also creating a random `challenge` and storing it inside a cookie to ensure that the
presentation results we are retrieving belong to the user in the current user session.
1. Create a new file at `src/app/api/request/route.ts` with the following content:
```tsx title="src/app/api/request/route.ts"
import { cookies } from "next/headers";
export async function POST() {
const sessionId = crypto.randomUUID();
const challenge = crypto.randomUUID();
const cookieStore = await cookies();
cookieStore.set("session_id", sessionId, {
httpOnly: true,
secure: true,
sameSite: true,
});
cookieStore.set("challenge", challenge, {
httpOnly: true,
secure: true,
sameSite: true,
});
return Response.json({ challenge }, { status: 201 });
}
```
While it's not strictly necessary to keep the `challenge` secret, you might want to only use a
single encrypted session cookie to not leak any information about your session management.
If you use a database to store the sessions state, you could use that database to store the
`challenge` against the user session.
### Retrieve verification results [#retrieve-verification-results]
Next we need to create an API route that will make a request to the MATTR VII verifier tenant and
retrieve the results of a specific presentation session based on its ID.
1. Create a new file at `src/app/api/results/[id]/route.ts` with the following content:
```tsx title="src/app/api/results/[id]/route.ts"
import { cookies } from "next/headers";
async function getToken(tenantUrl: string) {
const region = tenantUrl.split(".")[2];
const response = await fetch(
`https://auth.${region}.mattr.global/oauth/token`,
{
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({
client_id: process.env.MATTR_CLIENT_ID,
client_secret: process.env.MATTR_CLIENT_SECRET,
audience: tenantUrl,
grant_type: "client_credentials",
}),
},
);
const { access_token } = await response.json();
return access_token;
}
export async function GET(
_: Request,
{ params }: { params: Promise<{ id: string }> },
) {
const tenantUrl = process.env.NEXT_PUBLIC_TENANT_URL;
if (!tenantUrl) {
return Response.json({ error: "No tenant URL configured" });
}
const id = (await params).id;
const cookiesStore = await cookies();
if (!cookiesStore.get("session_id")) {
return Response.json(
{ error: "No valid session for user" },
{ status: 401 },
);
}
const response = await fetch(
`${tenantUrl}/v2/presentations/sessions/${id}/result`,
{
headers: {
authorization: `Bearer ${await getToken(tenantUrl)}`,
},
},
);
const results = await response.json();
if (results.challenge !== cookiesStore.get("challenge")?.value) {
return Response.json({ error: "Challenge does not match" });
}
return Response.json({ results });
}
```
The `getToken` function first uses project variables to retrieve a MATTR VII access token, as the results are retrieved from a protected endpoint.
The route must be called with a dynamic `id` route parameter. This is the unique identifier of
the presentation session. It is used in the authenticated request to the
`/v2/presentations/sessions/${id}/result` endpoint to retrieve the corresponding verification
results.
Your backend must then validate the returned results. The exact implementation would depend on
your user session management, but you should always ensure that:
* The request to retrieve the results comes from an active user session (as per its
`session_id`).
* The `challenge` included in the response from the MATTR VII tenant matches the `challenge` you
have stored for that active user session.
### Adjust front channel to retrieve results via backend [#adjust-front-channel-to-retrieve-results-via-backend]
Now that the backend is configured to retrieve the verification results, we need to adjust the front
channel web application so that it can retrieve these results from the backend.
1. Return to your `src/app/page.tsx` file and copy and paste the following code under the `Step 6.1 - Create createRequest function` comment.
```tsx title="src/app/page.tsx"
async function createRequest() {
const response = await fetch("/api/request", { method: "POST" });
const { challenge } = await response.json();
return challenge;
}
```
2. Replace the `challenge` assignment under the `Step 6.2 - Use challenge from backend` comment with
the following code to call the `createRequest` function to generate a challenge:
```tsx title="src/app/page.tsx"
challenge: await createRequest(),
```
3. Add the following code under the `Step 6.3 - Create retrieveResults function` comment to create a
new function that retrieves the verification results:
```tsx title="src/app/page.tsx"
const retrieveResults = useCallback(
async (id: string) => {
const response = await fetch(`api/results/${id}`);
const { results } = await response.json();
setResults(results as MATTRVerifierSDK.PresentationSessionResult);
},
[setResults],
);
```
This function uses the API route created [above](#retrieve-verification-results) to retrieve the
verification results and use `setResults` to assign them to the `results` state variable
4. Replace the `setResults` call under the
`Step 6.4 - Call retrieveResults function in cross-device workflow` with the following code to
call the new `retrieveResults` function in the callback of a cross-device workflow:
```tsx title="src/app/page.tsx"
retrieveResults(results.value.sessionId);
```
5. Replace the `setResults` function in the square brackets under the `// Step 5.3 - Add setResults`
comment with the `retrieveResults` function. Your code should look like this:
```tsx title="src/app/page.tsx"
}, [retrieveResults]) // requestCredentials dependency array
```
6. Replace the whole conditional around the `setResults` call under the
`Step 6.6 - Call retrieveResults function on same-device redirect` with the following code to
call the new `retrieveResults` function when the user is redirected to the app following a
same-device workflow:
```tsx title="src/app/page.tsx"
retrieveResults(results.value.sessionId);
```
7. Replace the `setResults` function in the brackets under the
`// Step 6.7 - Replace setResults function with retrieveResults function` comment with the
`retrieveResults` function. The line should look like this:
```tsx title="src/app/page.tsx"
}, [retrieveResults])
```
Your web application now has an integrated backend that can validate verification results before
passing them to the front channel, making the solution more secure.
### Test the backend interaction [#test-the-backend-interaction]
Let's test to ensure everything is working as expected.
1. Repeat the steps in the [front channel testing](#test-the-front-channel-interaction) step above.
2. The interaction should look the same, but behind the scenes it will actually be handled by your
backend.
## What's next? [#whats-next]
* You can adjust the app built in this tutorial to request and verify credentials using the Digital Credentials API, as detailed in the [DC API Guide](/docs/verification/remote-web-verifiers/dc-api/guide).
* This project was run and tested locally using tunneling services. To build similar online
verification capabilities into a production web application, all you need to do is replace the
`domain` and `redirectUri` with the corresponding values of your production web application.
* This project was created to showcase happy paths. It is recommended to review our comprehensive
[SDK reference documentation](https://api-reference-sdk.mattr.global/verifier-sdk-web/latest/index.html)
to learn more about available functions and classes.
* As always, feel free to [reach out](mailto:dev-support@mattr.global) if you have any questions or
encounter any issues we can help with.
# mDocs remote web app verification - OID4VP Workflow
URL: /docs/verification/remote-web-verifiers/workflow
mDocs are digital credentials based on the ISO/IEC
[18013-5](https://www.iso.org/standard/69084.html) standard and
[18013-7](https://www.iso.org/standard/91154.html) technical specification, designed to be stored on
a holder’s mobile device and support either in-person or remote
verification workflows.
The purpose of this page is to describe the end-to-end remote web app verification workflow, where a
user interacts with a web application to present an mDoc stored on their mobile device.
## Prerequisites [#prerequisites]
We recommend you make yourself familiar with the following concepts to support your understanding of
the implementation described in this page:
* What is [credential verification](/docs/verification)?
* What are [mDocs](/docs/concepts/mdocs)?
* What is [remote verification](/docs/verification/remote-overview)?
## Overview [#overview]
Remote web app verification of mDocs is based on
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html). Let's use a simple
example to illustrate how such a workflow might look like:
1. A relying party uses the Verifier Web SDK to embed a remote verification workflow into an
existing web application.
For example, a bank embeds remote verification as part of the workflow for opening a new bank
account on their online portal.
2. When a user interacts with the web application, a matching application installed on their mobile
device is invoked to request sharing information held in an mDoc.
In our example, the user attempts to open a new bank account in the bank's online portal, and is
requested to provide a supporting identity document, such as a Mobile Drivers License (mDL).
3. The user consents to sharing the requested information.
In our example, the user has already claimed an mDL as an mDoc into their mobile application in
a previous interaction, and can now share that credential to prove their address.
4. The user's mobile application shares the matching mDoc with the MATTR VII tenant configured by
the Verifier Web SDK to perform the verification workflow.
5. The MATTR VII tenant performs the required checks and returns the verification results via the
Verifier Web SDK to the web application.
6. The user journey continues based on the verification results and the relying party business logic
built into the web application.
In our example, pending successful verification of the mDoc, the user can proceed to opening a
new bank account.
## Detailed workflow [#detailed-workflow]
Let's take a closer look at the end-to-end workflow and explain the role of each component:
### The user triggers the workflow [#the-user-triggers-the-workflow]
The workflow is triggered when the user interacts with a web application that requires presenting
information stored in an mDoc.
For example, the user is attempting to open a bank account and is required to provide a supporting
identity document, which can be provided by presenting an mDoc stored in an application on their
mobile device.
In a same-device workflow, the user must begin the interaction in their
default web browser. Starting the interaction in a **different browser** or in **incognito mode** will lead to session
recognition issues and possible failures.
### The Verifier Web SDK starts a presentation-based verification session [#the-verifier-web-sdk-starts-a-presentation-based-verification-session]
The web application uses the Verifier Web SDK to send a request to a MATTR VII verifier tenant to
start a remote, presentation-based verification session.
This request is based on the Verifier Web SDK configuration that defines:
* The credential query:
* What credentials are required.
* What specific claims are required from these credentials.
* What MATTR VII tenant to interact with.
* A unique identifier of this web application.
* What [protocol](/docs/verification/remote-overview#verification-requests) to use for the interaction.
* An optional `state` value, used to correlate the verification session with a record in your own system. See [Correlating verification sessions](/docs/verification/remote-web-verifiers/guides/correlating-verification-sessions).
On the MATTR VII side, the tenant is configured to determine how to handle requests from different
[applications](/docs/verification/remote-verification-api-reference/verifier-applications#create-a-verifier-application)
based on the application identifier provided in the request.
### The MATTR VII verifier tenant responds with a link to invoke a matching application [#the-mattr-vii-verifier-tenant-responds-with-a-link-to-invoke-a-matching-application]
The MATTR VII verifier tenant responds with a URI, referred to as the *Authorization endpoint* in
OID4VP. This endpoint is configured when you
[create a trusted wallet provider](/docs/verification/remote-verification-api-reference/wallet-providers#create-a-wallet-provider)
and defines the URI to invoke an application based on its unique identifier.
As per OID4VP, the Authorization endpoint can be one of the following:
* **Default URI scheme**: This is a generic URI scheme defined by the OID4VP specification
(`openid-vp://`) that can be handled by any compliant application.
* If you are interacting with the link from an app that already recognizes this type of scheme, it
can handle the request immediately.
* If you are interacting with the link outside of the app (such as tapping a link in a
browser or scanning a QR code with the native camera), this will typically prompt the OS to
present options of any app that is registered to handle that scheme (on Android), or sometimes it
will automatically pick an app without enabling user selection (on iOS).
This approach is less secure as **any application** registered to handle this scheme could respond
to the request, which might lead to phishing attacks where a malicious app pretends to be a
trusted one.
* **Custom URI**: The [OAuth 2.0 for Native Apps](https://datatracker.ietf.org/doc/html/rfc8252) specification
proposes using more unique custom schemes to make it clearer that the link is intended for a
particular app. This uses reverse-domain schemes, such as `com.example.app://`.
This works in the same way as the default OID4VP custom scheme, but makes it less likely that
another app would be registered to handle the same reverse domain.
* **HTTP link**: Also called out in the [OAuth 2.0 for Native Apps](https://datatracker.ietf.org/doc/html/rfc8252)
specification, the most secure way to ensure only a specific app can handle a link is to register a
domain path to open your app via App Links on Android and Universal Links on iOS.
This is an HTTPS URL pointing to your domain, which the operating system verifies is
registered to only open a particular app. It provides the smoothest user experience as the OS can reliably
open the correct app without presenting choices.
HTTP links are recommended by the OID4VP specification as they are more secure. These URIs are
verified against the domain owner before launching the application, as the domain must host an
association file declaring the app's link. This ensures the provider owns both the app and the
domain.
Additionally, it is recommended to host a webpage for this URI to allow the user to continue the
interaction if the HTTP link fails to invoke the intended application. For example, the user
might not have the application installed and in this case the page should include a link to
enable them to download the app.
HTTP links require domain ownership and proper configuration of verification files on your web server. More information can be found in the following resources:
* [iOS - Universal Links](https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app)
* [Android - App Links](https://developer.android.com/training/app-links/verify-site-associations)
To use a custom URI scheme or App Link/Universal Link, the *Authorization endpoint* must be properly configured on the MATTR VII verifier tenant when creating a trusted wallet provider, for example:
* Custom URI scheme: `com.example.app://openid-vp` (where `com.example.app` is the app's reverse domain and `://openid-vp` is the app path that can handle the request)
* HTTP link: `https://example.com/openid-vp` (where `example.com` is a domain registered to open the specific app and configured with proper verification files)
The verifier's website can also include a UI element to allow users to select the app they want to use
for presenting credentials. Based on this selection, the Verifier Web SDK will identify the
application and send the corresponding identifier to the MATTR VII verifier tenant. The tenant will
then return the associated URI (Authorization endpoint).
Regardless of which URI scheme approach you choose, holder apps you expect to verify credentials from must be properly registered with the operating system to handle these schemes. See the following resources for platform-specific implementation guides:
* [Apple Docs - Defining a Custom URL Scheme for Your App](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app)
* [Android Docs - Create Deep Links to App Content](https://developer.android.com/training/app-links/deep-linking)
### The link is used to invoke a compliant application [#the-link-is-used-to-invoke-a-compliant-application]
The link is used by the Verifier Web SDK to invoke a compliant mobile application based on the
workflow type:
* **Same-device flow**: The link is
used to redirect the user to a compliant application on the same device. When using a Custom URI a
dialogue box appears on the mobile device, prompting them to launch a matching application (this
dialogue box is not displayed for HTTP links).
* **Cross-device flow**: The link
is rendered as a QR code and displayed in an embedded iframe. The user must then use a different
mobile device to scan the QR code and invoke a compliant mobile application.
The displayed QR code is automatically refreshed every 60 seconds to minimize risk to session
fixation attacks, where the attacker is trying to induce the user to scan a QR code and complete
a verification flow on their behalf.
Some of the iframe display properties can be adjusted as part of the verifier application
configuration on the MATTR VII tenant, to enable presenting different iframes to different
verifier applications.
The MATTR VII verifier tenant can be configured to support different remote verification workflows (e.g. same-device and/or cross-device) when interacting with different verifier applications.
### The mobile application makes a request to retrieve a request object [#the-mobile-application-makes-a-request-to-retrieve-a-request-object]
The invoked mobile application can now use the link to retrieve the request object from the MATTR
VII verifier tenant. This is a presentation request that defines what information is requested by
the verifier and for what purpose.
In [OAuth 2.0 terminology](https://datatracker.ietf.org/doc/html/rfc6749#section-1.1) this is
referred to as an Authorization request, made by the MATTR VII verifier tenant which acts as a
**Client**. The request is sent to the mobile application which acts as an **Authorization server**,
as it controls access to protected resources, in this case stored credentials.
### The MATTR VII tenant returns the request object [#the-mattr-vii-tenant-returns-the-request-object]
The MATTR VII verifier tenant generates the request object, signs it with its verifier certificate
and sends it as a response to the mobile application's request.
The mobile application will handle the received request object and perform the following:
* Establish trust with the relying party in one of the following methods:
* **Certificate trust model**: The mobile application compares the certificate used to sign the
request object against its list of trusted verifiers. This would be the
[`certificatePem`](/docs/verification/certificates/api-reference/verifier-root-ca-certificates#create-a-verifier-root-ca-certificate)
element of the verifier root CA, and must be provided by the verifier to the holder out of
band.
* **Domain trust model**: The mobile application retrieves the relying party metadata by making
a request to the `/.well-known/oauth-client` endpoint of the domain the request object is
coming from. By default this would be the verifier's
[`tenant_url`](/docs/platform-management/portal#getting-started). When using a
[Custom domain](/docs/platform-management/custom-domain-overview), you must configure a
[redirect](/docs/platform-management/custom-domain-guide#create-redirects-for-required-assets) for that path from your
custom domain to your MATTR VII verifier tenant.
* Gather credentials matching the information provided in the request object.
* Display this information to the holder and ask for consent to sharing it with the relying party.
The MATTR VII verifier tenant uses a [verifier root CA
certificate](/docs/verification/certificates/api-reference/verifier-root-ca-certificates#create-a-verifier-root-ca-certificate)
to identify itself when interacting with wallet applications.
### The mobile application sends an authorization response [#the-mobile-application-sends-an-authorization-response]
Once the user consents to sharing the information, the mobile application generates an authorization
response, which includes a signed verifiable presentation of matching credentials contained in a
`vp_token` parameter. The authorization response is encrypted and sent to the MATTR VII verifier
tenant.
Upon receiving the authorization response, the MATTR VII verifier tenant would decrypt it and
[verify](/docs/verification/remote-overview) the contained verifiable presentation.
This can include checking whether the certificate used to sign the credential matches an existing
Issuer in the MATTR VII tenant trusted issuers list.
### The MATTR VII verifier tenant returns the verification results [#the-mattr-vii-verifier-tenant-returns-the-verification-results]
The MATTR VII tenant returns the [verification](/docs/verification/remote-overview) results based on the verification workflow:
* **Same-device flow**: the MATTR
VII tenant responds with a redirect URI which is used by the mobile application to redirect the
user back to continue the interaction with the web application. This redirect opens a new tab in
the user's mobile browser, leaving the original tab (where the
[interaction began](#the-user-triggers-the-workflow)) still open.
* This redirect URI must be on the same domain as the one used to start the interaction with the web
application, to ensure session recognition. For example, if the user started the interaction on
`https://maggies.mattrlabs.com`, the redirect URI must also be on the `maggies.mattrlabs.com` domain, such as
`https://maggies.mattrlabs.com/verification-complete`.
* The redirect URI can be configured per verifier application, enabling redirecting users to
different targets based on the verifier application they are interacting with.
* The redirect URI will be handled by the device *default* web browser. This means that the user
must start their journey in their default web browser as well, otherwise it will be recognized
as a different session and result in a failure.
* It is recommended to advice users to refrain from starting the workflow in private/incognito
mode, as browser limitations might lead to unexpected behavior and possible failures.
* **Cross-device flow**: The Verifier Web SDK continuously checks for verification results. Once they are received they are passed to the web application and the cross-device workflow modal is closed.
When the web application has a backend, the MATTR VII verifier tenant can be configured to only send
the results to the backend rather than to the frontend directly. This enhances security as described
in the following flow:
1. The backend creates a unique challenge when the session is established.
2. The web application passes the unique challenge with the request to start a presentation session.
3. The web application is notified when the MATTR VII tenant had completed verification.
4. The web application passes the session ID to the backend.
5. The backend makes a
[request](/docs/verification/remote-verification-api-reference/presentation-sessions#retrieve-a-presentation-session-result)
to MATTR VII to retrieve the verification results for that session.
6. The MATTR VII tenant responds with the verification result and the unique challenge.
7. The backend compares the original and the received challenge to ensure the response can be
trusted.
8. The backend passes the verification results to the web application.
Results availability can be configured per verifier application.
### The web application surfaces verification results [#the-web-application-surfaces-verification-results]
The user journey can now proceed based on the web application business logic and the verification results received.
In our example, if the presented mDL was verified, the user can proceed to opening a bank account, whereas if verification failed they will receive an error message.
#### Understanding verification results [#understanding-verification-results]
Verification results indicate whether the presentation succeeded and whether the credentials were verified. Results can fall into two high-level categories:
**Presentation failed**: The holder was unable to complete the presentation workflow (e.g., wallet unavailable, session aborted, response errors).
**Presentation succeeded**: The holder completed the presentation workflow. Results may include verified credentials, credential errors (requested credentials not provided), claim errors (specific claims that failed verification), or a combination of these outcomes.
#### Result delivery [#result-delivery]
How your application receives verification results depends on your MATTR VII verifier application configuration:
**Front channel** (`resultAvailableInFrontChannel: true`): Results are returned directly to your web application through the Verifier Web SDK.
**Back channel** (`resultAvailableInFrontChannel: false`): Your backend retrieves results by calling the MATTR VII tenant's [presentation session result endpoint](/docs/api-reference/platform/mdocs-presentation-sessions/getPresentationResult).
Production implementations should use back channel delivery with challenge validation to protect against session replay attacks.
For detailed information about result structures, error types, and complete examples, see the [handling verification results guide](/docs/verification/remote-web-verifiers/guides/handling-verification-results).
# Android Verifier SDK v6.0.0 Migration Guide
URL: /docs/verification/sdks/android-6.0.0-migration-guide
## Overview [#overview]
This guide provides a comprehensive overview of the changes introduced in MobileCredentialVerifierSDK v6.0.0 for Android, including breaking changes, new features, and migration steps.
## Key Features [#key-features]
* **Improved reliability in contactless flows**: Enhanced BLE performance delivers more consistent proximity credential exchanges and faster engagements.
* **Stronger cryptography and standards alignment**: Updated COSE algorithms (as per RFC 9864) strengthen cryptographic compatibility and ensure continued compliance with evolving standards.
* **Simpler, Decoupled Releases**: The Android Verifier SDK no longer has a shared common module. This allows us to decouple the releases of Holder and Verifier and versions no longer need to match.
* **General stability and performance improvements**: Multiple refinements reduce integration friction, increase consistency across mobile environments, and improve overall user experience.
For a detailed list of changes included in this release, refer to the [SDK Changelog](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/).
## Breaking Changes [#breaking-changes]
This section outlines the breaking changes introduced in v6.0.0 that require updates to your existing implementation:
| # | Element | Change | Impact |
| - | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------- |
| 1 | `MobileCredentialVerifier.sendProximityPresentationRequest(skipStatusCheck = ...)` | Parameter renamed to `checkStatus = ...` with **inverted semantics** | All call sites using `skipStatusCheck = ...` must be updated. |
| 2 | `mattr.global.mobilecredentials.common` | Package path moved to `mattr.global.mobilecredentials.verifier` | All imports must be updated. |
## Bug Fixes [#bug-fixes]
* Fixed parsing of status lists that use a bit size other than 2.
* Fixed an issue where the Wallet could attempt to start a new session before the previous session had completed.
* Resolved BLE retry issues during proximity presentations that resulted in "SDK not ready (Engaging)" errors.
* Improved cross-device flow handling in DCM scenarios and resolved result mismatches.
* Corrected UI refresh issues following automatic session termination.
## Minimum Requirements [#minimum-requirements]
* Android 7 / Nougat / API 24.
* The Android Verifier SDK is built using Kotlin 2.0. This adds some intrinsic dependencies into your build tools.
* Kotlin 2.0 is supported from AGP version [8.5](https://developer.android.com/build/kotlin-support).
* AGP 8.5 is supported from Gradle version [8.7](https://developer.android.com/build/releases/about-agp) and Android Studio Koala [2024.1.1](https://developer.android.com/studio/releases).
## Migration Steps [#migration-steps]
### Rename any references to the `common` package to `verifier` [#rename-any-references-to-the-common-package-to-verifier]
The shared `common` module has been removed and bundled into the Android Verifier SDK. The package path has moved from `mattr.global.mobilecredentials.common` to `mattr.global.mobilecredentials.verifier`. Update all imports accordingly:
```diff
- import mattr.global.mobilecredentials.common.*
+ import mattr.global.mobilecredentials.verifier.*
```
This can be done with a global find and replace across your codebase. If you are using both the Holder and Verifier SDKs, you will need to limit your search to the relevant parts of your application.
### Remove the Common dependency from your project [#remove-the-common-dependency-from-your-project]
The Common dependency is now bundled into the Verifier SDK. Remove it from your project's `build.gradle` or `build.gradle.kts` file:
```diff
dependencies {
implementation("global.mattr.mobilecredentials:verifier:6.0.0")
- implementation("global.mattr.mobilecredentials:common:5.x.x")
}
```
### Update `sendProximityPresentationRequest` calls [#update-sendproximitypresentationrequest-calls]
The `skipStatusCheck` parameter has been renamed to `checkStatus` with inverted semantics. When `checkStatus` is `true` (default), the SDK will verify credential status. When `false`, it will skip status checking. Update all calls accordingly:
```diff
- val response = verifier.sendProximityPresentationRequest(request = requests, skipStatusCheck = true)
+ val response = verifier.sendProximityPresentationRequest(request = requests, checkStatus = false)
```
| Old Parameter | New Parameter | Mapping |
| ----------------------------------- | ------------------------------ | ------------------ |
| `skipStatusCheck = false` (default) | `checkStatus = true` (default) | No change needed |
| `skipStatusCheck = true` | `checkStatus = false` | Invert the boolean |
# Android Verifier SDK v7.0.0 Migration Guide
URL: /docs/verification/sdks/android-7.0.0-migration-guide
Description: A comprehensive guide to migrating to Android Verifier SDK v7.0.0, covering breaking changes, new features, and step-by-step migration instructions.
## Overview [#overview]
This guide provides a comprehensive overview of the changes introduced in the Android Verifier SDK
v7.0.0, including breaking changes, new features, and migration steps.
This release focuses on strengthening trust between your verifier application and your MATTR VII
tenant, improving consistency across platforms, and making verification results more predictable. The
headline change is **SDK Tethering**, which becomes **required** in this release: every SDK and app
instance is now registered with, and licensed by, your MATTR VII tenant at initialization.
Unlike the Holder SDK, where SDK Tethering is optional, **SDK Tethering is required for the Verifier
SDK** from v7.0.0. This builds on an existing requirement — remote mobile (app-to-app) verification
already required you to supply a `platformConfiguration` so the SDK could reach your MATTR VII tenant
to handle the backend verification. That `platformConfiguration` is now mandatory for all
initializations and additionally drives SDK Tethering.
## Key Features [#key-features]
* **SDK Tethering (required)**: The Android Verifier SDK is now tethered to a MATTR VII tenant, tying
each SDK/app instance to your tenant. On first initialization the SDK registers the app instance
with the tenant specified in `PlatformConfiguration` and obtains a license; on subsequent
initializations the existing license is renewed automatically. This lets you view registered and
active app instances directly from your tenant for operational insight, and establishes a remote
management channel we expect to extend in future releases (for example, remote syncing of trusted
issuer lists and eventing). The SDK uses [Key Attestation](https://source.android.com/docs/security/features/keystore/attestation)
during app registration. Network access is required when registration or renewal is performed.
* **Cross-platform alignment**: Verification result types and the revocation status list API have
been renamed and restructured to align with the iOS Verifier SDK, minimizing divergence for teams
maintaining cross-platform applications.
* **More predictable session results**: `OnlinePresentationSessionResult` and the revocation status
list refresh result are now sealed interfaces with explicit `Success` and `Failure` variants,
removing ambiguity from result handling.
* **Simpler remote mobile configuration**: The application ID for remote mobile (app-to-app)
verification is now taken from `PlatformConfiguration`, so it no longer needs to be passed on every
request.
* **General stability and performance improvements**: Multiple refinements reduce integration
friction, increase consistency, and improve overall reliability.
## Breaking Changes [#breaking-changes]
This section outlines the breaking changes introduced in v7.0.0 that require updates to your existing
implementation:
| # | Change | Impact |
| - | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | SDK Tethering is now required: `PlatformConfiguration` is mandatory on `initialize`, which now also registers the app instance and obtains a license | Always supply a `PlatformConfiguration`. Handle the new `InvalidLicenseException` and `FailedToRegisterException`, which can be thrown by `initialize` and by most SDK APIs. |
| 2 | `MobileCredentialVerifier.updateTrustedIssuerStatusLists` renamed to `refreshRevocationStatusLists` | Update all references to the new method name. |
| 3 | `MobileCredentialVerifier.getTrustedIssuerStatusListsCacheInfo` renamed to `getRevocationStatusListsCacheInfo` | Update all references to the new method name. |
| 4 | `TrustedIssuerStatusListsCacheInfo` renamed to `RevocationStatusListsCacheInfo` | Update all code that constructs or references this type. |
| 5 | `UpdateTrustedIssuerStatusListsResult` renamed to `RevocationStatusListsRefreshResult` and converted to a sealed interface; the `.success: Boolean` field is removed | Replace `if (result.success)` with `when`/`is` pattern matching over `Success` and `Failure`. |
| 6 | `OnlinePresentationSessionResult` converted to a sealed interface with `Success` and `Failure` variants | Replace field-based branching with `when`/`is` pattern matching. |
| 7 | `MobileCredentialResponse.credentials` and `MobileCredentialResponse.credentialErrors` are now required non-nullable `List` fields | Remove null-check branches and `?.` navigation; both arguments must be provided at construction. |
| 8 | `applicationId` parameter removed from `requestMobileCredentials` (remote mobile, app-to-app) | Remove the `applicationId` argument and supply it via `PlatformConfiguration` instead. |
## Migration Steps [#migration-steps]
### Create a verifier application on your MATTR VII tenant [#create-a-verifier-application-on-your-mattr-vii-tenant]
SDK Tethering requires a verifier application configured on the MATTR VII tenant your SDK connects
to. If you already use remote mobile (app-to-app) verification you will have created one; the same
application is reused for tethering. If you have not, create one now.
To register your Android application, make a request to create a verifier application:
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "My Android Verifier Application",
"type": "android",
"packageName": "com.yourcompany.verifierapp",
"packageSigningCertificateThumbprints": [
"1232584b6f6a892d356899fb9576c5f226a179e6199f2b7a1d837b5c234c5a8e"
]
}
```
* `name`: A unique name to identify your verifier application.
* `type`: Must be `android` for an Android application.
* `packageName`: The package name of your Android application.
* `packageSigningCertificateThumbprints`: SHA-256 hex-encoded fingerprints of the signing key
certificates used to sign your APK or app bundle. This ensures the tenant only accepts requests
from known and trusted applications. Refer to [Android app signing](/docs/verification/android-app-signing)
for more information.
The response will include a unique `id` for your application, used by the SDK to identify and
authenticate your application.
### Supply `PlatformConfiguration` and handle tethering errors at initialization [#supply-platformconfiguration-and-handle-tethering-errors-at-initialization]
`PlatformConfiguration` is now required on `initialize`. Previously it was optional and only used for
remote mobile (app-to-app) verification flows; it now also drives SDK Tethering, registering the app
instance with your MATTR VII tenant and obtaining a license on first initialization.
Always pass a `PlatformConfiguration`, and handle the new `InvalidLicenseException` and
`FailedToRegisterException` that `initialize` can now throw:
```diff
- val platformConfiguration = PlatformConfiguration(
- tenantHost = URL("https://your-tenant.vii.mattr.global")
- )
- MobileCredentialVerifier.initialize(context, platformConfiguration)
+ val platformConfiguration = PlatformConfiguration(
+ tenantHost = URL("https://your-tenant.vii.mattr.global"),
+ applicationId = "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
+ )
+ try {
+ MobileCredentialVerifier.initialize(context, platformConfiguration)
+ } catch (e: FailedToRegisterException) {
+ // Registration with the MATTR VII tenant failed — check connectivity and configuration
+ } catch (e: InvalidLicenseException) {
+ // The SDK license is missing, invalid, or expired
+ }
```
* `tenantHost`: The URL of your MATTR VII tenant where your verifier application is configured.
* `applicationId`: The `id` of your configured MATTR VII verifier application.
The majority of the SDK's other APIs can now also throw `InvalidLicenseException` when a valid
license is not present. Network access is required the first time the SDK initializes (for
registration) and when the license is renewed on subsequent initializations. Update your error
handling, logging, analytics, and support diagnostics to account for these cases.
### Remove the `applicationId` argument from `requestMobileCredentials` [#remove-the-applicationid-argument-from-requestmobilecredentials]
`MobileCredentialVerifier.requestMobileCredentials` (remote mobile, app-to-app) no longer takes an
`applicationId` parameter. It now uses the application ID supplied in `PlatformConfiguration` during
initialization. Remove the argument from your call sites:
```diff
val result = verifier.requestMobileCredentials(
request = listOf(mobileCredentialRequest),
challenge = challenge,
- applicationId = "your-application-id"
)
```
Ensure you provide `applicationId` via `PlatformConfiguration` (see the previous step) instead.
### Update revocation status list method and type names [#update-revocation-status-list-method-and-type-names]
The revocation status list management API has been renamed from `TrustedIssuer`-prefixed terminology
to `Revocation` terminology to better reflect its purpose — managing the lists used to check the
revocation status of credentials. Update all call sites:
```diff
- val result = verifier.updateTrustedIssuerStatusLists()
+ val result = verifier.refreshRevocationStatusLists()
- val cacheInfo = verifier.getTrustedIssuerStatusListsCacheInfo()
+ val cacheInfo = verifier.getRevocationStatusListsCacheInfo()
```
| Old | New |
| ---------------------------------------- | ------------------------------------- |
| `updateTrustedIssuerStatusLists()` | `refreshRevocationStatusLists()` |
| `getTrustedIssuerStatusListsCacheInfo()` | `getRevocationStatusListsCacheInfo()` |
| `TrustedIssuerStatusListsCacheInfo` | `RevocationStatusListsCacheInfo` |
| `UpdateTrustedIssuerStatusListsResult` | `RevocationStatusListsRefreshResult` |
### Update revocation status list refresh result handling [#update-revocation-status-list-refresh-result-handling]
`RevocationStatusListsRefreshResult` (formerly `UpdateTrustedIssuerStatusListsResult`) has been
converted from a data class to a sealed interface with `Success` and `Failure` variants, and the
`.success: Boolean` field has been removed. Replace boolean branching with `when`/`is` pattern
matching:
```diff
- val result = verifier.refreshRevocationStatusLists()
- if (result.success) {
- // Handle success
- } else {
- // Handle failure
- }
+ when (val result = verifier.refreshRevocationStatusLists()) {
+ is RevocationStatusListsRefreshResult.Success -> {
+ // Handle successful refresh
+ }
+ is RevocationStatusListsRefreshResult.Failure -> {
+ // result.failedLists is available here
+ }
+ }
```
### Update online presentation session result handling [#update-online-presentation-session-result-handling]
`OnlinePresentationSessionResult` has been converted from a data class to a sealed interface with
`Success` and `Failure` variants. Replace field-based branching with `when`/`is` pattern matching:
```diff
- val result = ... // OnlinePresentationSessionResult
- if (result.mobileCredentialResponse != null) {
- // Use result.mobileCredentialResponse
- } else {
- // Use result.error
- }
+ when (result) {
+ is OnlinePresentationSessionResult.Success -> {
+ // result.mobileCredentialResponse is guaranteed
+ }
+ is OnlinePresentationSessionResult.Failure -> {
+ // result.error is guaranteed
+ }
+ }
```
### Remove optional handling on `MobileCredentialResponse` collections [#remove-optional-handling-on-mobilecredentialresponse-collections]
`MobileCredentialResponse.credentials` and `MobileCredentialResponse.credentialErrors` are now
required non-nullable `List` fields, instead of nullable fields. Remove null checks and `?.`
navigation, and provide both arguments when constructing the type:
```diff
- for (credential in response.credentials.orEmpty()) { ... }
+ for (credential in response.credentials) { ... }
```
# iOS Verifier SDK v5.0.0 Migration Guide
URL: /docs/verification/sdks/ios-5.0.0-migration-guide
## Overview [#overview]
This guide provides a comprehensive overview of the changes introduced in MobileCredentialVerifierSDK v5.0.0 for iOS, including breaking changes, new features, and migration steps.
## Key Features [#key-features]
* **Lifecycle management improvements**: New `destroy()` method enables complete SDK reset, plus `deinitialize()` no longer requires `@MainActor` context for improved flexibility.
* **Improved reliability in contactless flows**: Enhanced BLE performance delivers more consistent proximity credential exchanges and faster engagements.
* **Enhanced storage reliability**: Fixed intermittent storage initialization errors during app launch by migrating key storage from `UserDefaults` to the Keychain with explicit availability checks.
* **Better timeout handling**: Proximity presentation session timeouts are now correctly propagated to the caller instead of being silently ignored.
For a detailed list of changes included in this release, refer to the [SDK Changelog](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/changelog).
## Breaking Changes [#breaking-changes]
This section outlines the breaking changes introduced in v5.0.0 that require updates to your existing implementation:
| # | Symbol | Change | Impact |
| - | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------- |
| 1 | `MobileCredentialVerifier.sendProximityPresentationRequest(request:skipStatusCheck:)` | Parameter renamed to `checkStatus:` with **inverted semantics** | All call sites using `skipStatusCheck:` must be updated. |
| 2 | `MobileCredentialVerifier.deinitialize()` | No longer `@MainActor` | Can now be called from any actor context. |
| 3 | `MobileCredentialVerifierError` | Introduced two new enum cases: `.storageInitializedInBackground`, `.sdkInitialized` | Exhaustive `switch` statements must add new cases. |
## New Additions [#new-additions]
### Methods [#methods]
| Method | Purpose |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `destroy()` | Destroys SDK instance and deletes all certificates from storage. Throws `sdkInitialized` if called while SDK is initialized. |
### Enum Cases [#enum-cases]
| Type | New Case | Description |
| ------------------------------- | --------------------------------- | --------------------------------------------------------------------- |
| `MobileCredentialVerifierError` | `.storageInitializedInBackground` | SDK initialized without keychain access (e.g., during app prewarming) |
| `MobileCredentialVerifierError` | `.sdkInitialized` | Operation requires SDK to be deinitialized first |
## Deprecations [#deprecations]
No new deprecations.
## Bug Fixes [#bug-fixes]
* Fixed intermittent "unable to initialize storage" errors during app launch. The issue was caused by using `UserDefaults` for `keyId` storage, which does not guarantee persistence—particularly during iOS app prewarming when protected data may be unavailable. The SDK now stores the `keyId` in the Keychain and performs explicit availability checks before initialization, throwing a new `storageInitializedInBackground` error when the keychain is inaccessible.
* Fixed an issue where `ProximityPresentationSession` timeouts were silently ignored instead of throwing an error. The SDK now correctly propagates timeout errors to the caller when the session times out waiting for a response.
## Minimum Requirements [#minimum-requirements]
* iOS 15+ for core SDK functionality.
## Migration Steps [#migration-steps]
### Update `sendProximityPresentationRequest` calls [#update-sendproximitypresentationrequest-calls]
The `skipStatusCheck` parameter has been renamed to `checkStatus` with inverted semantics. When `checkStatus` is `true` (default), the SDK will verify credential status. When `false`, it will skip status checking. Update all calls accordingly:
```diff
- let response = try await verifier.sendProximityPresentationRequest(request: requests, skipStatusCheck: true)
+ let response = try await verifier.sendProximityPresentationRequest(request: requests, checkStatus: false)
```
| Old Parameter | New Parameter | Mapping |
| ---------------------------------- | ----------------------------- | ------------------ |
| `skipStatusCheck: false` (default) | `checkStatus: true` (default) | No change needed |
| `skipStatusCheck: true` | `checkStatus: false` | Invert the boolean |
### Handle new error cases [#handle-new-error-cases]
If you have exhaustive `switch` statements on `MobileCredentialVerifierError`, you must add handlers for the two new error cases:
```diff
switch error {
case .sdkNotInitialized:
// Handle not initialized
case .storageInitialization:
// Handle storage error
+ case .storageInitializedInBackground:
+ // SDK initialized during app prewarming — prompt user to retry
+ case .sdkInitialized:
+ // Operation requires deinitialize() first
// ... other cases
}
```
**Error handling guidance:**
* `.storageInitializedInBackground`: This error occurs when the SDK is initialized during iOS app prewarming, before the keychain is accessible. Listen for `UIApplication.protectedDataDidBecomeAvailableNotification` and re-initialize the SDK when keychain access becomes available.
* `.sdkInitialized`: This error is thrown by `destroy()` when called while the SDK is still initialized. Call `deinitialize()` first, then retry the operation.
### Use `destroy()` for complete reset (optional) [#use-destroy-for-complete-reset-optional]
If you need to completely reset SDK state and delete all stored certificates and credentials, you can now call the new `destroy()` method. This is optional and should be used with caution as it will remove all data:
```swift
// Ensure SDK is deinitialized first
await MobileCredentialVerifier.shared.deinitialize()
// Then destroy all stored data
try MobileCredentialVerifier.shared.destroy()
// Re-initialize when needed
try MobileCredentialVerifier.shared.initialize()
```
### Handle `deinitialize()` actor context change [#handle-deinitialize-actor-context-change]
The `deinitialize()` method is no longer restricted to `@MainActor`. You can now call it from any context without dispatching to the main actor:
```diff
- await MainActor.run {
- await MobileCredentialVerifier.shared.deinitialize()
- }
+ await MobileCredentialVerifier.shared.deinitialize()
```
# iOS Verifier SDK v6.0.0 Migration Guide
URL: /docs/verification/sdks/ios-6.0.0-migration-guide
Description: A comprehensive guide to migrating to iOS Verifier SDK v6.0.0, covering breaking changes, new features, and step-by-step migration instructions.
## Overview [#overview]
This guide provides a comprehensive overview of the changes introduced in the iOS Verifier SDK
v6.0.0, including breaking changes, new features, and migration steps.
This release focuses on strengthening trust between your verifier application and your MATTR VII
tenant, improving consistency across platforms, and making verification results more predictable. The
headline change is **SDK Tethering**, which becomes **required** in this release: every SDK and app
instance is now registered with, and licensed by, your MATTR VII tenant at initialization.
Unlike the Holder SDK, where SDK Tethering is optional, **SDK Tethering is required for the Verifier
SDK** from v6.0.0. This builds on an existing requirement — remote mobile (app-to-app) verification
already required you to supply a `platformConfiguration` so the SDK could reach your MATTR VII tenant
to handle the backend verification. That `platformConfiguration` is now mandatory for all
initializations and additionally drives SDK Tethering.
## Key Features [#key-features]
* **SDK Tethering (required)**: The iOS Verifier SDK is now tethered to a MATTR VII tenant, tying
each SDK/app instance to your tenant. On first initialization the SDK registers the app instance
with the tenant specified in `PlatformConfiguration` and obtains a license; on subsequent
initializations the existing license is renewed automatically. This lets you view registered and
active app instances directly from your tenant for operational insight, and establishes a remote
management channel we expect to extend in future releases (for example, remote syncing of trusted
issuer lists and eventing). The SDK uses [App Attest](https://developer.apple.com/documentation/devicecheck/establishing-your-app-s-integrity)
during app registration when available on the device. Network access is required when registration
or renewal is performed.
* **Asynchronous initialization**: `initialize` is now asynchronous, aligning the SDK with modern
Swift concurrency and the registration/licensing work performed during tethering.
* **Cross-platform alignment**: Verification result types and the revocation status list API have
been renamed and restructured to align with the Android Verifier SDK, minimizing divergence for
teams maintaining cross-platform applications.
* **Simpler challenge handling for remote verification**: The `challenge` parameter for remote mobile
(app-to-app) verification is now optional; when omitted, the SDK generates a cryptographically
secure challenge for you.
* **Clearer connectivity error reporting**: Remote mobile verification now surfaces a dedicated
`connectivityError` when the internet connection is lost mid-flow.
* **General stability and performance improvements**: Multiple refinements reduce integration
friction, increase consistency, and improve overall reliability.
## Breaking Changes [#breaking-changes]
This section outlines the breaking changes introduced in v6.0.0 that require updates to your existing
implementation:
| # | Change | Impact |
| -- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | SDK Tethering is now required: `platformConfiguration` is mandatory on `initialize`, which now also registers the app instance and obtains a license | Always supply a `platformConfiguration`. Handle the new `invalidLicense` and `failedToRegister` errors, which can be thrown by `initialize` and by most SDK APIs. |
| 2 | `initialize` is now asynchronous | Add `await` to all call sites and call `initialize` from an asynchronous context. |
| 3 | `MobileCredentialVerifierError.platformConfigurationInvalid` removed | Remove handling for this case; `fetchAppleWalletConfiguration(request:merchantId:)` no longer throws it. |
| 4 | `VerificationResult` renamed to `MobileCredentialVerificationResult`, with `.reason` renamed to `.failureType` | Update all type references, rename `.reason` to `.failureType`, and remove use of `VerificationFailedReason`. |
| 5 | `TrustedCertificateVerificationResult.reason` renamed to `.failureType` | Rename `.reason` to `.failureType` and remove use of `VerificationFailedReason`. |
| 6 | `MobileCredentialVerificationFailureType` now serializes as a `{type, message}` object instead of a plain raw-value string | Update any storage or transport layer that persists or forwards these serialized values. |
| 7 | `TrustedCertificateVerificationFailureType` now serializes as a `{type, message}` object instead of a plain raw-value string | Update any storage or transport layer that persists or forwards these serialized values. |
| 8 | Revocation status list methods and types renamed from `TrustedIssuer`-prefixed terminology to `Revocation` terminology | Rename `updateTrustedIssuerStatusLists` → `refreshRevocationStatusLists`, `getTrustedIssuerStatusListsCacheInfo` → `getRevocationStatusListsCacheInfo`, and the corresponding return types. |
| 9 | `RevocationStatusListsRefreshResult` and `OnlinePresentationSessionResult` converted from structs with optional properties to `@frozen` enums with `success` and `failure` cases | Replace property-based branching with `switch`/`case` pattern matching. |
| 10 | `applicationId` parameter removed from `fetchAppleWalletConfiguration` and `requestMobileCredentials` | Remove the `applicationId` argument from these call sites; the SDK now uses the `applicationId` from `PlatformConfiguration`. |
## Migration Steps [#migration-steps]
### Create a verifier application on your MATTR VII tenant [#create-a-verifier-application-on-your-mattr-vii-tenant]
SDK Tethering requires a verifier application configured on the MATTR VII tenant your SDK connects
to. If you already use remote mobile (app-to-app) verification you will have created one; the same
application is reused for tethering. If you have not, create one now.
To register your iOS application, make a request to create a verifier application:
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "My iOS Verifier Application",
"type": "ios",
"bundleId": "com.yourcompany.verifierapp",
"teamId": "YOUR_APPLE_TEAM_ID"
}
```
* `name`: A unique name to identify your verifier application.
* `type`: Must be `ios` for an iOS application.
* `bundleId`: The Bundle ID of your iOS app (must match your Xcode project configuration).
* `teamId`: Your Apple Developer Team ID.
The response will include a unique `id` for your application. This is the `applicationId` you supply
in `PlatformConfiguration` at initialization, which the SDK now uses for all flows (including remote
mobile verification).
### Supply `platformConfiguration` and make `initialize` asynchronous [#supply-platformconfiguration-and-make-initialize-asynchronous]
`initialize` is now asynchronous and `platformConfiguration` is required. Previously,
`platformConfiguration` was optional and only used for remote mobile (app-to-app) verification flows;
it now also drives SDK Tethering, registering the app instance with your MATTR VII tenant and
obtaining a license on first initialization.
Add `await`, call `initialize` from an asynchronous context, and always pass a
`platformConfiguration`:
```diff
- let platformConfiguration = PlatformConfiguration(
- tenantHost: URL(string: "https://your-tenant.vii.mattr.global")!
- )
- try MobileCredentialVerifier.shared.initialize(platformConfiguration: platformConfiguration)
+ let platformConfiguration = PlatformConfiguration(
+ tenantHost: URL(string: "https://your-tenant.vii.mattr.global")!,
+ applicationId: "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
+ )
+ try await MobileCredentialVerifier.shared.initialize(platformConfiguration: platformConfiguration)
```
* `tenantHost`: The URL of your MATTR VII tenant where your verifier application is configured.
* `applicationId`: The `id` of your configured iOS Verifier Application.
Network access is required the first time the SDK initializes (for registration) and when the
license is renewed on subsequent initializations.
### Handle license and registration errors [#handle-license-and-registration-errors]
Because tethering registers and licenses the SDK, `initialize` can now throw
`MobileCredentialVerifierError.invalidLicense` and `MobileCredentialVerifierError.failedToRegister`.
The majority of the SDK's other APIs can now also throw `invalidLicense` when a valid license is not
present. Update your error handling, logging, analytics, and support diagnostics to account for these
cases:
```diff
do {
try await MobileCredentialVerifier.shared.initialize(platformConfiguration: platformConfiguration)
} catch {
switch error {
+ case MobileCredentialVerifierError.failedToRegister:
+ // Registration with the MATTR VII tenant failed — check connectivity and configuration
+ case MobileCredentialVerifierError.invalidLicense:
+ // The SDK license is missing, invalid, or expired
// ... other cases
}
}
```
The `MobileCredentialVerifierError.platformConfigurationInvalid` case has been removed and is no
longer thrown by `fetchAppleWalletConfiguration(request:merchantId:)`. Remove any handling for it.
### Update `VerificationResult` to `MobileCredentialVerificationResult` [#update-verificationresult-to-mobilecredentialverificationresult]
The `VerificationResult` type has been renamed to `MobileCredentialVerificationResult` and aligned
structurally with Android. Its `reason` property has been renamed to `failureType`, typed directly as
`MobileCredentialVerificationFailureType?` rather than the now-removed `VerificationFailedReason`
wrapper. `MobileCredential.verificationResult` and `MobileCredentialPresentation.verificationResult`
now return `MobileCredentialVerificationResult`:
```diff
- let result: VerificationResult = credential.verificationResult
+ let result: MobileCredentialVerificationResult = credential.verificationResult
- let failure = result.reason
+ let failure = result.failureType
```
Replace all references to `VerificationResult` with `MobileCredentialVerificationResult`, rename
`.reason` to `.failureType`, and remove any usage of `VerificationFailedReason`.
### Rename `TrustedCertificateVerificationResult.reason` to `failureType` [#rename-trustedcertificateverificationresultreason-to-failuretype]
The same `.reason` → `.failureType` rename applies to `TrustedCertificateVerificationResult`. Its
`failureType` is now typed directly as `TrustedCertificateVerificationFailureType?` instead of the
now-removed `VerificationFailedReason` wrapper:
```diff
- let failure = trustedCertificateResult.reason
+ let failure = trustedCertificateResult.failureType
```
### Update failure-type serialization handling [#update-failure-type-serialization-handling]
`MobileCredentialVerificationFailureType` and `TrustedCertificateVerificationFailureType` now encode
and decode as a `{type, message}` object instead of a plain raw-value string:
```diff
- "TrustedIssuerCertificateNotFound"
+ {"type": "TrustedIssuerCertificateNotFound", "message": "Trusted issuer certificate not found"}
```
If you persist or forward the serialized value of either failure type, update your storage or
transport layer to produce and consume the new format.
### Update revocation status list method and type names [#update-revocation-status-list-method-and-type-names]
The revocation status list management API has been renamed from `TrustedIssuer`-prefixed terminology
to `Revocation` terminology to better reflect its purpose — managing the lists used to check the
revocation status of credentials. Update all call sites to use the new method names and return types:
```diff
- let result = try await verifier.updateTrustedIssuerStatusLists()
+ let result = try await verifier.refreshRevocationStatusLists()
- let cacheInfo = verifier.getTrustedIssuerStatusListsCacheInfo()
+ let cacheInfo = try verifier.getRevocationStatusListsCacheInfo()
```
| Old | New |
| ---------------------------------------- | ------------------------------------- |
| `updateTrustedIssuerStatusLists()` | `refreshRevocationStatusLists()` |
| `getTrustedIssuerStatusListsCacheInfo()` | `getRevocationStatusListsCacheInfo()` |
| `UpdateTrustedIssuerStatusListsResult` | `RevocationStatusListsRefreshResult` |
| `TrustedIssuerStatusListsCacheInfo` | `RevocationStatusListsCacheInfo` |
### Update result-type handling for `@frozen` enums [#update-result-type-handling-for-frozen-enums]
`RevocationStatusListsRefreshResult` and `OnlinePresentationSessionResult` have been converted from
structs with optional properties to `@frozen` enums with `success` and `failure` cases. Replace
property-based branching with `switch`/`case` pattern matching.
`RevocationStatusListsRefreshResult.success` carries `nextUpdate: Date?`, and `.failure` carries
`nextUpdate: Date?` and `failedLists: [String: [String]]`:
```diff
- let result = try await verifier.refreshRevocationStatusLists()
- if result.success {
- // Handle success
- } else {
- // Handle failure
- }
+ switch try await verifier.refreshRevocationStatusLists() {
+ case .success(let nextUpdate):
+ // All status lists refreshed; schedule the next refresh before nextUpdate
+ case .failure(let nextUpdate, let failedLists):
+ // failedLists holds the URIs that failed to refresh, keyed by trusted issuer certificate ID
+ }
```
`OnlinePresentationSessionResult.success` carries `sessionId: String`, `challenge: String?`, and
`mobileCredentialResponse: MobileCredentialResponse?`; `.failure` carries `sessionId: String`,
`challenge: String?`, and `error: OnlinePresentationResultError`:
```diff
- if let response = result.mobileCredentialResponse {
- // Use response
- } else {
- // Use result.error
- }
+ switch result {
+ case .success(_, _, let mobileCredentialResponse):
+ // mobileCredentialResponse?.credentials holds the presented credentials
+ case .failure(_, _, let error):
+ // error.type and error.message describe the failure
+ }
```
### Remove the `applicationId` argument from `fetchAppleWalletConfiguration` and `requestMobileCredentials` [#remove-the-applicationid-argument-from-fetchapplewalletconfiguration-and-requestmobilecredentials]
The `applicationId` parameter has been removed from `fetchAppleWalletConfiguration` and
`requestMobileCredentials`. The SDK now uses the `applicationId` supplied in `PlatformConfiguration`
during initialization. Remove the argument from your call sites:
```diff
let result = try await verifier.requestMobileCredentials(
request: [mobileCredentialRequest],
challenge: challenge,
- applicationId: "your-application-id"
)
```
Ensure you provide `applicationId` via `PlatformConfiguration` (see the earlier step) instead.
### (Optional) Simplify challenge handling for remote mobile verification [#optional-simplify-challenge-handling-for-remote-mobile-verification]
The `challenge` parameter in `requestMobileCredentials` for remote mobile (app-to-app) verification
is now optional. When omitted or left blank, the SDK generates a cryptographically secure random
32-byte challenge automatically, so you no longer need to manage challenge generation yourself:
```diff
- let result = try await verifier.requestMobileCredentials(
- request: [mobileCredentialRequest],
- challenge: UUID().uuidString
- )
+ let result = try await verifier.requestMobileCredentials(
+ request: [mobileCredentialRequest]
+ )
```
This is an optional improvement; supplying your own `challenge` continues to work.
### (Optional) Handle connectivity errors in remote mobile verification [#optional-handle-connectivity-errors-in-remote-mobile-verification]
`requestMobileCredentials` for remote mobile (app-to-app) verification now throws
`MobileCredentialVerifierError.connectivityError` if the internet connection is lost during the flow.
Handle this case to provide clear feedback and retry guidance to your users:
```diff
do {
let result = try await verifier.requestMobileCredentials(request: requests)
} catch {
+ if case MobileCredentialVerifierError.connectivityError = error {
+ // Prompt the user to check their connection and retry
+ }
}
```
# Verifier Mobile SDKs Overview
URL: /docs/verification/sdks/overview
## Overview [#overview]
The mDocs Verifier SDK are based on the
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html) and
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) standards, which establish an
interoperable digital representation of mobile-based credentials such as mobile drivers licenses
(mDL). However, these SDKs can extend the same technology and architecture to more than just mDLs,
but rather any conforming mobile document ([mDoc](/docs/concepts/mdocs)) - a term defined in ISO/IEC 18013-5.
The mDocs Verifier SDKs are available for React Native, iOS, and Android. They help developers add
mDocs verification capabilities to their apps, allowing secure and privacy preserving verification
of presented mDocs in various interoperable workflows.
To get started with any of our mDocs Verifier SDKs, please [contact
us](mailto:sales@mattr.global).
## SDK Capabilities [#sdk-capabilities]
The mDocs Verifier SDKs offer tools to assist developers integrating the following capabilities into
their applications:
* Interface with an mDoc holder to request presentations of issued mDocs via:
* Proximity verification: Verify an mDoc presented in-person as per
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
* Remote mobile app verification: Remotely verify an mDoc presented from a different app
installed on the same mobile device (as per
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html)).
* Manage a list of trusted issuer certificates which presented mDocs can be validated against.
* Manage status lists which can be used to check mDocs' [revocation status](/docs/issuance/revocation/overview).
* Interface with an mDoc holder to request presentations of issued mDocs via:
* Proximity verification: Verify an mDoc presented in-person as per
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
* Remote mobile app verification: Remotely verify an mDoc presented from a different app
installed on the same mobile device (as per
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html)).
* Manage a list of trusted issuer certificates which presented mDocs can be validated against.
* Manage status lists which can be used to check mDocs' [revocation status](/docs/issuance/revocation/overview).
* Interface with an mDoc holder to request presentations of issued mDocs via:
* Proximity verification: Verify an mDoc presented in-person as per
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
* Manage a list of trusted issuer certificates which presented mDocs can be validated against.
* Manage status lists which can be used to check mDocs' [revocation status](/docs/issuance/revocation/overview).
## Supported features [#supported-features]
### Supported ISO/IEC 18013-5 Features [#supported-isoiec-18013-5-features]
Below is a summary of ISO/IEC 18013-5 features supported by the mDocs Verifier SDKs:
| Feature | Supported options | Default |
| :------------------------------ | :----------------------------------------------------------------- | :------------------------------- |
| Device engagement | QR code | QR code |
| Device retrieval data transport | BLE with either `mDocPeripheralServer` or `mDocCentralClient` mode | Determined by holder application |
| Ephemeral session key curve | Any NIST P-\* keys | Determined by holder application |
| IACA public key curves | P-256, P-384, P-521 | Determined by Issuer |
| Device authentication mode | Digital Signature or ECDH-agreed MAC | Determined by holder application |
| Feature | Supported options | Default |
| :------------------------------ | :----------------------------------------------------------------- | :------------------------------- |
| Device engagement | QR code and NFC | QR code |
| Device retrieval data transport | BLE with either `mDocPeripheralServer` or `mDocCentralClient` mode | Determined by holder application |
| Ephemeral session key curve | Any NIST P-\* keys | Determined by holder application |
| IACA public key curves | P-256, P-384, P-521 | Determined by Issuer |
| Device authentication mode | Digital Signature or ECDH-agreed MAC | Determined by holder application |
| Feature | Supported options | Default |
| :------------------------------ | :----------------------------------------------------------------- | :------------------------------- |
| Device engagement | QR code | QR code |
| Device retrieval data transport | BLE with either `mDocPeripheralServer` or `mDocCentralClient` mode | Determined by holder application |
| Ephemeral session key curve | Any NIST P-\* keys | Determined by holder application |
| IACA public key curves | P-256, P-384, P-521 | Determined by Issuer |
| Device authentication mode | Digital Signature or ECDH-agreed MAC | Determined by holder application |
## System requirements [#system-requirements]
The SDK is developed in the [Swift](https://developer.apple.com/swift/) programming language and is
meant for integration into iOS applications developed in Swift and/or Objective-C. Specifically, it
currently only supports applications developed in iOS 15 and above.
This SDK is developed in the Kotlin programming language and is meant for integration into Android
applications. It currently supports Android 7 (API level 24) and above.
This SDK is meant for integration into React Native applications using React Native 0.78 and above.
Supported operating systems are:
* iOS 15 or higher.
* Android 7 or higher.
## Dependencies [#dependencies]
This section lists all dependencies for using mDocs Verifier SDKs.
**Third party dependencies**
* [CBORCoding](https://github.com/SomeRandomiOSDev/CBORCoding) (MIT license).
* [swift-certificates](https://github.com/apple/swift-certificates.git) 1.7.0 (Apache-2.0 License).
* [swift-asn1](https://github.com/apple/swift-asn1) 1.3.1 (Apache-2.0 License).
**Apple frameworks**
* [Security](https://developer.apple.com/documentation/security)
* [CryptoKit](https://developer.apple.com/documentation/cryptokit/)
* [LocalAuthentication](https://developer.apple.com/documentation/localauthentication)
* [CoreBluetooth](https://developer.apple.com/documentation/corebluetooth)
* [Combine](https://developer.apple.com/documentation/combine)
* [OSLog](https://developer.apple.com/documentation/oslog)
* [UIKit](https://developer.apple.com/documentation/uikit)
* [AppKit (on macOS)](https://developer.apple.com/documentation/appkit)
* [PassKit](https://developer.apple.com/documentation/passkit)
* [SwiftUI](https://developer.apple.com/documentation/swiftui)
**Toolchain dependencies**
* Swift 5.10.
* iOS support: The SDK functionality is only available for devices from iOS 15 onwards.
* Xcode: The SDK is built with the latest stable Xcode available in GitHub Actions (setup-xcode action with `latest-stable` label). The current version is Xcode 26.0.0 (17A324).
* macOS 15.
**Kotlin, AGP, Gradle, and Android Studio**
The Android Verifier SDK is built using Kotlin 2.0. This adds some intrinsic dependencies into your build tools.
* Kotlin 2.0 is supported from AGP version [8.5](https://developer.android.com/build/kotlin-support).
* AGP 8.5 is supported from Gradle version [8.7](https://developer.android.com/build/releases/about-agp) and Android Studio Koala [2024.1.1](https://developer.android.com/studio/releases).
**Runtime**
A list of runtime dependencies and licenses is generated at build time and packaged in `res/raw/dependencies_licenses.html`. The majority are Kotlin and Android, the rest are listed below:
* [CBOR-Java](https://github.com/peteroupc/CBOR-Java) - [Public Domain](https://github.com/peteroupc/CBOR-Java/blob/master/LICENSE.md)
* [Timber](https://github.com/JakeWharton/timber) - [Apache 2.0](https://github.com/JakeWharton/timber/blob/trunk/LICENSE.txt)
**Standard libraries**
* [androidx.activity:activity-ktx:1.9.0](https://developer.android.com/jetpack/androidx/releases/activity#1.9.0)
* [androidx.annotation:annotation:1.8.1](https://developer.android.com/jetpack/androidx/releases/annotation#1.8.1)
* [androidx.appcompat:appcompat:1.7.0](https://developer.android.com/jetpack/androidx/releases/appcompat#1.7.0)
* [androidx.biometric:biometric-ktx:1.2.0-alpha05](https://developer.android.com/jetpack/androidx/releases/biometric#1.2.0-alpha05)
* [androidx.browser:browser:1.8.0](https://developer.android.com/jetpack/androidx/releases/browser#1.8.0)
* [androidx.core:core-ktx:1.15.0](https://developer.android.com/jetpack/androidx/releases/core#1.15.0)
* [androidx.credentials:credentials-play-services-auth:1.5.0](https://developer.android.com/jetpack/androidx/releases/credentials#1.5.0)
* [androidx.credentials:credentials:1.5.0](https://developer.android.com/jetpack/androidx/releases/credentials#1.5.0)
* [androidx.fragment:fragment:1.5.7](https://developer.android.com/jetpack/androidx/releases/fragment#1.5.7)
* [org.jetbrains.kotlin:kotlin-reflect:1.9.22](https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-reflect/1.9.22)
* [org.jetbrains.kotlin:kotlin-stdlib:2.0.0](https://kotlinlang.org/)
* [org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3](https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.7.3)
* [org.jetbrains.kotlinx:kotlinx-datetime:0.4.0](https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-datetime-jvm/0.4.0)
* [org.jetbrains.kotlinx:kotlinx-io-bytestring:0.6.0](https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-io-bytestring-tvosarm64/0.6.0)
* [org.jetbrains.kotlinx:kotlinx-io-core:0.6.0](https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-io-core/0.6.0)
* [org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3](https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-serialization-json/1.6.3)
**Third-party libraries**
* [com.jakewharton.timber:timber:5.0.1](https://mvnrepository.com/artifact/com.jakewharton.timber/timber/5.0.1)
* [com.upokecenter:cbor:4.5.2](https://mvnrepository.com/artifact/com.upokecenter/cbor/4.5.2)
* None.
## Versions [#versions]
Below are the available versions of the mDocs Verifier Mobile SDKs, including the current active
version, supported versions, and those that have reached end-of-life (EOL).
| Major version | Status | Latest release | End of Life date | Documentation |
| ------------- | ----------- | -------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| v6 | Active | 6.0.0 | - | [SDK Docs](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/6.0.0/documentation/mobilecredentialverifiersdk/) |
| v5 | Maintenance | 5.1.1 | 26-09-2026 | [SDK Docs](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/5.1.1/documentation/mobilecredentialverifiersdk/) |
| v4 | End of Life | 4.1.0 | 13-05-2026 | - |
| v3 | End of Life | 3.0.0 | 7-10-2025 | - |
| v2 | End of Life | 2.0.0 | 26-08-2025 | - |
| v1 | End of Life | 1.0.1 | 05-05-2025 | - |
| Major version | Status | Latest release | End of Life date | Documentation |
| ------------- | ----------- | -------------- | ---------------- | -------------------------------------------------------------------------------------------- |
| v7 | Active | 7.0.0 | - | [SDK Docs](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/7.0.0/) |
| v6 | Maintenance | 6.1.1 | 26-09-2026 | [SDK Docs](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/6.1.1/) |
| v5 | End of Life | 5.3.2 | 13-05-2026 | - |
| v4 | End of Life | 4.1.1 | 30-12-2025 | - |
| v3 | End of Life | 3.0.0 | 7-10-2025 | - |
| v2 | End of Life | 2.0.0 | 26-08-2025 | - |
| v1 | End of Life | 1.0.1 | 05-05-2025 | - |
| Major version | Status | Latest release | End of Life date | Documentation |
| ------------- | ----------- | -------------- | ---------------- | ----------------------------------------------------------------------------------------------------------- |
| v9 | Active | 9.0.3 | - | [SDK Docs](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/9.0.3/index.html) |
| v8 | End of Life | 8.1.1 | 23-06-2026 | - |
| v7 | End of Life | 7.1.0 | 12-12-2025 | - |
| v6 | End of Life | 6.0.0 | 26-08-2025 | - |
| v5 | End of Life | 5.0.0 | 05-05-2025 | - |
| v4 | End of Life | 4.1.1 | 13-04-2025 | - |
| v3 | End of Life | 3.0.0 | 15-02-2025 | - |
| v2 | End of Life | 2.0.0 | 17-12-2024 | - |
| v1 | End of Life | 1.0.1 | 05-07-2024 | - |
Release candidates (RC) are pre-release versions that may contain new features or changes that are
not yet fully tested. They are intended for testing purposes, are not subject to our SLA and should
not be used in production environments.
# React Native Verifier SDK v9.0.0 Migration Guide
URL: /docs/verification/sdks/react-native-9.0.0-migration-guide
Description: A guide to help developers migrate from React Native Verifier SDK v8.x to v9.0.0, including breaking changes, new features, and best practices.
## Overview [#overview]
This guide provides a comprehensive overview of the changes introduced in React Native Verifier SDK v9.0.0, including breaking changes, new features, and migration steps.
## Key Features [#key-features]
* **App to app verification**: The React Native Verifier SDK can now be used to request credentials from another app on the same device using OID4VP. This allows you to build verification flows directly into your apps and have a holder app on the same device respond.
* **Verify with Apple Wallet (iOS Only)**: The SDK can now request and verify a credential directly from an Apple Wallet using the Verify with Wallet API. Only available for iOS 16 and above.
* **Improved reliability in contactless flows**: Enhanced BLE performance delivers more consistent proximity credential exchanges and faster engagements.
* **Status Lists Draft 14 Support**: The SDK now supports the Token Status List Draft 14 specification while maintaining existing support for Draft 3.
* **Stronger cryptography and standards alignment**: Updated COSE algorithms (as per RFC 9864) strengthen cryptographic compatibility and ensure continued compliance with evolving standards.
* **General stability and performance improvements**: Multiple refinements reduce integration friction, increase consistency across mobile environments, and improve overall user experience.
For a detailed list of changes included in this release, refer to the [SDK Changelog](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/index.html#md:change-log).
## Breaking Changes [#breaking-changes]
| # | Element | Change | Impact |
| - | ------------------------------------------------------ | ------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| 1 | `initialize()` | Now accepts options and returns a `Result` type. | All call sites must handle the result and possible errors. |
| 2 | `sendProximityPresentationRequest` | Option renamed from `skipStatusCheck` to `checkStatus` with inverted semantics. | All call sites using `skipStatusCheck` must be updated. |
| 3 | `ProximityPresentationSessionTerminationErrorType` | New `Exception` value added. | Exhaustive switches must handle new case. |
| 4 | NFC error listener in `registerForNfcDeviceEngagement` | Now routes parse failures to `onError` instead of rethrowing. | Update error handling logic accordingly. |
## New Additions [#new-additions]
### Functions [#functions]
| Function | Description | Platform |
| ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
| `fetchAppleWalletConfiguration(options)` | Fetches Apple Wallet configuration needed before initiating the Apple Wallet verification flow. Returns an `AppleWallet` object. | iOS only |
| `handleDeepLink(options)` | Handles a deep link URL to continue the online presentation flow. | iOS only |
| `requestMobileCredentials(options)` | Remote app-to-app credential verification via Digital Credential Manager / OID4VP. Returns `OnlinePresentationSessionResult`. | Android (DCM/OID4VP), iOS (OID4VP only) |
| `destroy()` | Destroys the verifier SDK instance. Returns an error if called while the SDK is initialized; deinitialize the SDK before calling. | All |
| `getCurrentLogFilePath()` | Returns path to the Verifier SDK log file. | All |
### Updated Function Signatures [#updated-function-signatures]
* `initialize(options?)` — new `InitializeOptions` parameter:
* `loggerConfiguration?: LoggerConfiguration` — `logLevel`, `callbackLogLevel`, optional `logDir`, optional `callback`
* `platformConfiguration?: PlatformConfiguration` — `tenantHost: string` (required for `requestMobileCredentials`)
### New initialize Options [#new-initialize-options]
* `loggerConfiguration?: LoggerConfiguration` — configure SDK logging: logLevel, callbackLogLevel, logDir, callback on log events.
* `platformConfiguration?: PlatformConfiguration` — set MATTR VII tenant host for remote credential requests.
### New Types & Enums [#new-types--enums]
* **Online presentation (remote/app-to-app):**
* `OnlinePresentationSessionResult` — `{ sessionId, challenge?, mobileCredentialResponse?, error? }`
* `OnlinePresentationResultError` — `{ type: OnlinePresentationResultErrorType, message }`
* `OnlinePresentationResultErrorType` — `SessionAborted | VerificationError | ResponseError | WalletUnavailable | Unknown`
* **Apple Wallet:**
* `AppleWallet` — object with `requestMobileCredentials(challenge): Promise>`
* `RequestMobileCredentialsWithAppleWalletError` / `RequestMobileCredentialsWithAppleWalletErrorType`
* **`requestMobileCredentials` options & errors:**
* `RequestMobileCredentialsOptions` — `{ request, applicationId, walletProviderId?, challenge }`
* `RequestMobileCredentialsErrorType`
* `RequestMobileCredentialsError`
* **`handleDeepLink`:**
* `HandleDeepLinkOptions` — `{ url: string }`
* **`fetchAppleWalletConfiguration`:**
* `FetchAppleWalletConfigurationOptions` — `{ request, applicationId, merchantId }`
* `FetchAppleWalletConfigurationError` / `FetchAppleWalletConfigurationErrorType`
* **`initialize`:**
* `InitializeOptions`, `LoggerConfiguration`, `PlatformConfiguration`, `LogLevel` (`Off | Error | Warn | Info | Debug | Verbose`)
* `InitializeErrorType` = `SdkInitialized | StorageInitializedInBackground`
### New Error Values [#new-error-values]
| Location | New values |
| -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MobileCredentialVerifierErrorType` | `Connectivity`, `StorageInitializedInBackground`, `SdkInitialized`, `PlatformNotSupported`, `PlatformConfigurationInvalid`, `SessionTimedOut`, `SessionAborted`, `DigitalCredentialManager`, `UserCanceled`, `FailedToRequestMobileCredentials`, `AppleWalletNotAvailable` |
| `ProximityPresentationSessionTerminationErrorType` | `Exception` |
## Minimum Requirements [#minimum-requirements]
**iOS**
* iOS 15+ for core SDK functionality.
**Android**
* Android 7 / Nougat / API 24.
* The underlying Android Verifier SDK is built using Kotlin 2.0. This adds some intrinsic dependencies into your build tools.
* Kotlin 2.0 is supported from AGP version [8.5](https://developer.android.com/build/kotlin-support).
* AGP 8.5 is supported from Gradle version [8.7](https://developer.android.com/build/releases/about-agp) and Android Studio Koala [2024.1.1](https://developer.android.com/studio/releases).
## Migration Steps [#migration-steps]
### Update `initialize()` usage [#update-initialize-usage]
The `initialize()` function now accepts an optional options object and returns a `Result`. Update your code to handle the result and possible errors:
```diff
- await initialize();
+ const result = await initialize(options);
+ if (result.isErr()) {
+ // Handle error: result.error
+ }
```
### Update `sendProximityPresentationRequest` calls [#update-sendproximitypresentationrequest-calls]
The `skipStatusCheck` parameter has been renamed to `checkStatus` with inverted semantics. When `checkStatus` is `true` (default), the SDK will verify credential status. When `false`, it will skip status checking. Update all calls accordingly:
```diff
- const response = await sendProximityPresentationRequest({ ..., skipStatusCheck: true });
+ const response = await sendProximityPresentationRequest({ ..., checkStatus: false });
```
| Old Parameter | New Parameter | Mapping |
| ----------------------------------- | ------------------------------ | ------------------ |
| `skipStatusCheck = false` (default) | `checkStatus = true` (default) | No change needed |
| `skipStatusCheck = true` | `checkStatus = false` | Invert the boolean |
### Handle new `ProximityPresentationSessionTerminationErrorType.Exception` case [#handle-new-proximitypresentationsessionterminationerrortypeexception-case]
If you switch over `ProximityPresentationSessionTerminationErrorType`, add handling for the new `Exception` value.
### Update NFC error handling [#update-nfc-error-handling]
The NFC error listener in `registerForNfcDeviceEngagement` now routes parse failures to `onError` instead of rethrowing. Update your error handling logic accordingly.
# Getting started with the Verifier SDKs
URL: /docs/verification/sdks/sdk-getting-started
Description: Set up access to the MATTR Pi mDocs Verifier SDKs, configure SDK tethering, and initialize the SDK in your mobile application.
This guide walks you through the steps required to start building with the MATTR Pi mDocs Verifier
SDKs. By the end, your mobile application will be ready to verify credential presentations. For the
native iOS and Android Verifier SDKs, this includes tethering your application to a MATTR VII
tenant. The React Native Verifier SDK is not tethered: for in-person (proximity) verification it
does not require a MATTR VII tenant or platform configuration, and only needs a tenant for remote
mobile (app-to-app) verification. React Native differences are called out at each step below.
### Request SDK access [#request-sdk-access]
To access the MATTR Pi mDocs Verifier SDKs, complete the
[Get Started form](/docs/resources/get-started) with the following details:
* Your organization name and contact information.
* The platform(s) you plan to build for (iOS, Android, or React Native).
* A brief description of your use case.
### Create a MATTR VII tenant [#create-a-mattr-vii-tenant]
The native iOS and Android Verifier SDKs require a MATTR VII tenant that serves as the backend for
SDK operations including tethering and credential verification. For React Native, a tenant is only
required for remote mobile (app-to-app) verification.
If you are building React Native for in-person verification only, you can skip this step.
1. Log into the [MATTR Portal](https://portal.mattr.global).
2. Select the **Create/switch tenant** button on the top-right side of the screen.\
The *All tenants* panel is displayed, listing any existing tenants.
3. Select the **Create new** button.\
The *New tenant* form is displayed.
4. Use the *Region* dropdown list to select the region your tenant will be hosted in.
5. Use the *Tenant subdomain* text box to insert a subdomain for your tenant (e.g. `in-person-verification`).
6. Select the **Create** button to create the new tenant.
7. Copy the displayed tenant information (`audience`, `auth_url`, `tenant_url`, `client_id` and `client_secret`).
### Create a Verifier Application [#create-a-verifier-application]
The iOS and Android Verifier SDKs are **tethered** to a MATTR VII tenant. On initialization, the SDK registers your app instance with the
tenant and obtains a license, so SDK Tethering must be configured before you initialize the SDK. For
a full explanation of tethering and the capabilities it enables, see [SDK Tethering](/docs/verification/sdks/sdk-tethering).
To tether the SDK, create a Verifier Application on your MATTR VII tenant for the platform you are
building.
Make a request of the following structure to create an iOS Verifier Application configuration on your MATTR VII tenant:
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "My iOS Verifier Application",
"type": "ios",
"bundleId": "com.yourcompany.verifierapp",
"teamId": "YOUR_APPLE_TEAM_ID",
"appAttest": {
"required": false,
"environment": "development"
}
}
```
* `name`: A unique name to identify this Verifier Application.
* `type`: Must be `ios`.
* `bundleId`: The Bundle ID of your iOS app (must match your Xcode project configuration).
* `teamId`: Your Apple Developer Team ID (must match the Team ID used to sign your app).
* `appAttest`: App Attest configuration for the iOS verifier application:
* `required`: When `true`, the app instance must provide a valid App Attest attestation during
registration and token renewal. When `false`, the app can fall back to assertion-only
authentication. See [Attestation vs Assertion](/docs/verification/sdks/sdk-tethering#attestation-vs-assertion-fall-back) for more details.
* `environment`: The App Attest environment (`development` or `production`). Apple recommends
using `development` for testing and `production` for distribution builds.
A successful response returns a `201` status code with the created Verifier Application:
```json title="Response"
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c", // [!code focus]
"name": "My iOS Verifier Application",
"type": "ios",
"bundleId": "com.yourcompany.verifierapp",
"teamId": "YOUR_APPLE_TEAM_ID",
"appAttest": {
"required": false,
"environment": "development"
}
}
```
* `id`: A unique identifier for the Verifier Application (generated by the tenant). You must use this value when initializing the SDK so that it can correctly
identify and authenticate your application.
Make a request of the following structure to create an Android Verifier Application configuration on your MATTR VII tenant:
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "My Android Verifier Application",
"type": "android",
"packageName": "com.yourcompany.verifierapp",
"packageSigningCertificateThumbprints": [
"1232584B6F6A892D356899FB9576C5F226A179E6199F2B7A1D837B5C234C5A8E"
],
"keyAttestation": {
"required": false
},
"openid4vpConfiguration": {
"redirectUri": "com.yourcompany.verifierapp://oid4vp-callback"
}
}
```
* `name`: A unique name to identify this Verifier Application.
* `type`: Must be `android`.
* `packageName`: The package name of your Android application.
* `packageSigningCertificateThumbprints`: SHA-256 hex-encoded fingerprints of the signing key
certificates used to sign your APK or app bundle. This ensures the tenant only accepts requests
from known and trusted applications. Refer to
[Android app signing](/docs/verification/android-app-signing) for more information.
* `keyAttestation`: Key Attestation configuration for the Android verifier application:
* `required`: When `true`, the app instance must provide a valid Key Attestation during
registration and token renewal. When `false`, the app can register and renew tokens using
just an authentication assertion. See [Attestation vs Assertion](/docs/verification/sdks/sdk-tethering#attestation-vs-assertion-fall-back) for more details.
* `openid4vpConfiguration.redirectUri`: Required by the create-application endpoint, which needs at
least one of `openid4vpConfiguration` or `dcApiConfiguration`. In-person proximity verification
does not use this redirect, so any valid custom-scheme URI is accepted here.
A successful response returns a `201` status code with the created Verifier Application:
```json title="Response"
{
"id": "a82bfa46-72a0-4cde-b6cb-2a0de7e2f3c4", // [!code focus]
"name": "My Android Verifier Application",
"type": "android",
"packageName": "com.yourcompany.verifierapp",
"packageSigningCertificateThumbprints": [
"1232584B6F6A892D356899FB9576C5F226A179E6199F2B7A1D837B5C234C5A8E"
],
"keyAttestation": {
"required": false
}
}
```
* `id`: A unique identifier for the Verifier Application (generated by the tenant). You must use this value when initializing the SDK so that it can correctly
identify and authenticate your application.
SDK Tethering is currently not required for the React Native Verifier SDK.
### Initialize the SDK [#initialize-the-sdk]
When you initialize the SDK, you must provide a `PlatformConfiguration` object with your tenant host and the `id` of the Verifier Application you created. This allows the SDK to register the app instance with your tenant and obtain a license to operate.
Initialize the SDK with your platform configuration. The `initialize` method is asynchronous, so call it from an asynchronous context:
```swift title="Initialization"
let platformConfig = PlatformConfiguration(
tenantHost: URL(string: "https://your-tenant.vii.mattr.global")!,
applicationId: "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
)
try await MobileCredentialVerifier.shared.initialize(
platformConfiguration: platformConfig
)
```
* `tenantHost`: The URL of your MATTR VII tenant. This must be the tenant where your iOS Verifier
Application is configured.
* `applicationId`: The `id` of your configured iOS Verifier Application.
Initialize the SDK with your platform configuration:
```kotlin title="Initialization"
val platformConfig = PlatformConfiguration(
tenantHost = URL("https://your-tenant.vii.mattr.global"),
applicationId = "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
)
MobileCredentialVerifier.initialize(context, platformConfig)
```
* `tenantHost`: The URL of your MATTR VII tenant where your Android Verifier Application is configured.
* `applicationId`: The `id` of your configured Android Verifier Application.
The React Native Verifier SDK is **not** tethered, so initialization does not register an app
instance or require a `platformConfiguration` object.
## Next steps [#next-steps]
Your application is now initialized and tethered to your MATTR VII tenant, ready to verify
credentials. Explore the following resources to start building:
* In-person verification:
* [Quickstart](/docs/verification/in-person-quickstart): Run a sample in-person verifier app end-to-end.
* [Tutorial](/docs/verification/in-person-tutorial): Detailed walkthrough of building an app that can verify credentials in-person using Bluetooth proximity presentations.
* Remote mobile verification:
* [Quickstart](/docs/verification/remote-mobile-verifiers/quickstart): Run a sample remote mobile verifier app end-to-end.
* [Tutorial](/docs/verification/remote-mobile-verifiers/tutorial): Detailed walkthrough of building an app that can request and verify credentials from a wallet app on the same device (app-to-app).
# Configure SDK logging
URL: /docs/verification/sdks/sdk-logging
Description: Learn how to configure logging in the MATTR Verifier SDKs, including log levels, callback handlers, and accessing log files across iOS, Android, and React Native platforms.
The MATTR Verifier SDKs include a built-in logging system that records internal SDK operations. This
is useful for debugging integration issues, monitoring SDK behavior, and capturing diagnostic
information during development and testing.
By default, SDK logs are stored on the device. The SDK itself does not transmit logs to any
external service, although your application can choose to forward log events elsewhere if you
register a callback.
## What information the SDK can log [#what-information-the-sdk-can-log]
The SDK can log information about its internal operations, including errors and warnings encountered during SDK operations.
All log entries include the log level and a descriptive message. This information helps you
diagnose issues and understand how the SDK operates within your application.
## Log levels [#log-levels]
The SDK supports the following log levels, ordered from most to least verbose:
| Level | Description |
| --------- | -------------------------------------------------------------------------- |
| `Verbose` | Fine-grained informational events, most detailed output. |
| `Debug` | Detailed information useful during development. |
| `Info` | General informational messages about SDK operations. |
| `Warning` | Potentially harmful situations or unexpected behavior (`Warn` in Android). |
| `Error` | Error events that might still allow the SDK to continue running. |
| `Assert` | Severe error events that indicate a critical failure (Android only). |
| `Off` | Disables logging entirely. |
The SDK uses the configured log level as a threshold: it records log events at the specified level
and any less verbose levels. For example, setting the level to `Info` captures `Info`, `Warning`,
`Error`, and `Assert` events, but not `Debug` or `Verbose`.
## Configure logging at initialization [#configure-logging-at-initialization]
You can configure logging behavior by passing a `loggerConfiguration` object to the SDK's
`initialize` method. This configuration accepts two separate log levels:
* **`logLevel`**: Controls which log events are written to the log file.
* **`callbackLogLevel`**: Controls which log events trigger the optional callback function.
```swift title="Configure logging during initialization"
try mobileCredentialVerifier.initialize(
loggerConfiguration: LoggerConfiguration( // [!code highlight]
logLevel: .Info, // [!code highlight]
callbackLogLevel: .Warning // [!code highlight]
) // [!code highlight]
)
```
* `logLevel`: Sets the minimum level for writing log entries to the log file. Defaults to `.Off`.
* `callbackLogLevel`: Sets the minimum level for invoking the callback closure. Defaults to `.Off`.
Refer to the
[`LoggerConfiguration`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/loggerconfiguration)
reference documentation for additional details.
```kotlin title="Configure logging during initialization"
mobileCredentialVerifier.initialize(
loggerConfiguration = Logger.LoggerConfiguration(
logLevel = Logger.LogLevel.INFO,
callbackLogLevel = Logger.LogLevel.WARN
),
// ...
)
```
* `logLevel`: Sets the minimum level for writing log entries to the log file and Logcat. Defaults to `Logger.LogLevel.OFF`.
* `callbackLogLevel`: Sets the minimum level for invoking the callback function. Defaults to `Logger.LogLevel.OFF`.
* `logDir`: Optional. Specifies a local directory to store log files. If not provided, logs won't be stored to file.
Refer to the
[`Logger.LoggerConfiguration`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/m-docs%20-verifier%20-s-d-k/global.mattr.mobilecredential.verifier.util/-logger/-logger-configuration/index.html)
reference documentation for additional details.
```ts title="Configure logging during initialization"
import { LogLevel } from "@mattrglobal/mobile-credential-verifier-react-native"
const initializeResult = await mobileCredentialVerifier.initialize({
loggerConfiguration: { // [!code highlight]
logLevel: LogLevel.Info, // [!code highlight]
callbackLogLevel: LogLevel.Warn, // [!code highlight]
}, // [!code highlight]
})
if (initializeResult.isErr()) {
const { error } = initializeResult
// handle error scenarios
return
}
```
* `logLevel`: Sets the minimum level for writing log entries to the log file and console. Defaults to `LogLevel.Off`.
* `callbackLogLevel`: Sets the minimum level for invoking the callback function. Defaults to `LogLevel.Off`.
* `logDir`: Optional. Specifies a directory to store log files. On iOS, a default directory is used. On Android, logs won't be stored to file if not provided.
Refer to the
[`LoggerConfiguration`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/types/LoggerConfiguration.html)
reference documentation for additional details.
## Handle log events with a callback [#handle-log-events-with-a-callback]
You can register a callback function during initialization to receive log events in real time. This
allows your application to process log events as they occur, for example to forward them to a custom
logging service, display them in a debug console, or filter specific events for monitoring.
The callback is only invoked for log events at or above the `callbackLogLevel` threshold.
```swift title="Register a logging callback"
try mobileCredentialVerifier.initialize(
loggerConfiguration: LoggerConfiguration(
logLevel: .Info,
callbackLogLevel: .Warning,
callback: { logEvent in // [!code highlight]
print("[\(logEvent.level)] \(logEvent.message)") // [!code highlight]
} // [!code highlight]
)
)
```
The callback receives a
[`LogEvent`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/logevent)
object with the following properties:
* `level`: The [`LogLevel`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/loglevel) of the event.
* `message`: A string describing the log event.
```kotlin title="Register a logging callback"
mobileCredentialVerifier.initialize(
loggerConfiguration = Logger.LoggerConfiguration(
logLevel = Logger.LogLevel.INFO,
callbackLogLevel = Logger.LogLevel.WARN,
callback = { priority, tag, message, throwable -> // [!code highlight]
Log.d("VerifierSDK", "[$tag] $message") // [!code highlight]
} // [!code highlight]
),
// ...
)
```
The callback function receives the following parameters:
* `priority`: An integer representing the log priority level.
* `tag`: An optional string tag identifying the log source.
* `message`: A string describing the log event.
* `throwable`: An optional `Throwable` associated with the log event (for error-level logs).
```ts title="Register a logging callback"
import { LogLevel } from "@mattrglobal/mobile-credential-verifier-react-native"
const initializeResult = await mobileCredentialVerifier.initialize({
loggerConfiguration: {
logLevel: LogLevel.Info,
callbackLogLevel: LogLevel.Warn,
callback: (log) => { // [!code highlight]
console.log(`[${log.logLevel}] ${log.tag ?? ""}: ${log.message ?? ""}`) // [!code highlight]
}, // [!code highlight]
},
})
if (initializeResult.isErr()) {
const { error } = initializeResult
// handle error scenarios
return
}
```
The callback receives a log object with the following properties:
* `logLevel`: The [`LogLevel`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/enums/LogLevel.html) of the event.
* `message`: An optional string describing the log event.
* `tag`: An optional string tag identifying the log source.
## Access the log file [#access-the-log-file]
The SDK writes log entries to a file that you can access for debugging and diagnostics. The log file
contains entries from the previous two calendar days.
```swift title="Get the log file path"
let logFilePath = mobileCredentialVerifier.getCurrentLogFilePath()
```
The
[`getCurrentLogFilePath`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/mobilecredentialverifier/getcurrentlogfilepath\(\))
method returns the file path as a string, or `nil` if no log file is available.
To read the logs, use the returned file path to load the file contents into `Data`, then decode the
data into text and split it into individual log messages as needed.
```kotlin title="Get the log file path"
val logFilePath = mobileCredentialVerifier.getLog()
```
The
[`getLog`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/m-docs%20-verifier%20-s-d-k/global.mattr.mobilecredential.verifier/-mobile-credential-verifier/get-log.html)
method returns the full path of the log file (with a `.log` extension), or `null` if no log file is
available.
The file contains log entries from the previous two calendar days.
```ts title="Get the log file path"
const logFilePath = await mobileCredentialVerifier.getCurrentLogFilePath()
```
The
[`getCurrentLogFilePath`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/functions/getCurrentLogFilePath.html)
method returns a `Promise` resolving to the log file path string, or `null` if no log file is
available.
# SDK Tethering
URL: /docs/verification/sdks/sdk-tethering
Description: Learn how MATTR Verifier SDKs are tethered to a MATTR VII tenant through Verifier Application configuration, enabling operational insights and licensing.
SDK Tethering ties each SDK/app instance to a MATTR VII tenant. Tethering establishes a trust
relationship between your mobile application and the MATTR VII tenant, enabling the following
capabilities:
* **Operational insights**: View details about registered and active app instances directly from
your tenant.
* **Licensing**: On first initialization, the SDK registers the app instance with your tenant and
obtains a license. The majority of the SDK's APIs require a valid license to operate.
* **Remote management channel**: SDK Tethering establishes a channel that we expect to extend in
the future with capabilities such as remote syncing of trusted issuer lists and eventing.
SDK Tethering is required from the following SDK versions:
* **iOS Verifier SDK**: 6.0.0
* **Android Verifier SDK**: 7.0.0
## How it works [#how-it-works]
The tethering process involves three steps:
1. **Configure a Verifier Application on your MATTR VII tenant**: You register your mobile app by
creating a Verifier Application, identified by the bundle identifier and team ID (iOS) or the package
fingerprint (Android).
2. **Initialize the SDK with your tenant details**: When you initialize the SDK in your app, you
pass the details of the MATTR VII tenant and the Verifier Application you configured on it.
3. **Automatic communication**: Once initialized, instances of your app will automatically
communicate with the configured MATTR VII tenant and retrieve the required tokens to operate
and make requests to the tenant when required.
## Token validity and offline use [#token-validity-and-offline-use]
The tokens issued during this process have configurable validity periods controlled by the
`maxTimeOfflineInSecs` field on your Verifier Application configuration. This means your app can
function without internet connectivity to meet different use cases:
* **Minimum**: 1 day (86400 seconds)
* **Maximum**: 30 days (2592000 seconds)
* **Default**: 7 days (604800 seconds)
When the license token expires, the SDK must reconnect to the MATTR VII tenant to renew it. Network
access is required when registration or renewal is performed.
## Configuring SDK Tethering [#configuring-sdk-tethering]
### Configure Verifier Applications [#configure-verifier-applications]
Make a request of the following structure to create an iOS Verifier Application configuration on your MATTR VII tenant:
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "My iOS Verifier Application",
"type": "ios",
"bundleId": "com.yourcompany.verifierapp",
"teamId": "YOUR_APPLE_TEAM_ID",
"appAttest": {
"required": false,
"environment": "development"
}
}
```
* `name`: A unique name to identify this Verifier Application.
* `type`: Must be `ios`.
* `bundleId`: The Bundle ID of your iOS app (must match your Xcode project configuration).
* `teamId`: Your Apple Developer Team ID (must match the Team ID used to sign your app).
* `appAttest`: App Attest configuration for the iOS verifier application:
* `required`: When `true`, the app instance must provide a valid App Attest attestation during
registration and token renewal. When `false`, the app can fall back to assertion-only
authentication. See [Attestation vs Assertion](/docs/verification/sdks/sdk-tethering#attestation-vs-assertion-fall-back) for more details.
* `environment`: The App Attest environment (`development` or `production`). Apple recommends
using `development` for testing and `production` for distribution builds.
A successful response returns a `201` status code with the created Verifier Application:
```json title="Response"
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c", // [!code focus]
"name": "My iOS Verifier Application",
"type": "ios",
"bundleId": "com.yourcompany.verifierapp",
"teamId": "YOUR_APPLE_TEAM_ID",
"appAttest": {
"required": false,
"environment": "development"
}
}
```
* `id`: A unique identifier for the Verifier Application (generated by the tenant). You must use this value when initializing the SDK so that it can correctly
identify and authenticate your application.
Make a request of the following structure to create an Android Verifier Application configuration on your MATTR VII tenant:
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "My Android Verifier Application",
"type": "android",
"packageName": "com.yourcompany.verifierapp",
"packageSigningCertificateThumbprints": [
"1232584B6F6A892D356899FB9576C5F226A179E6199F2B7A1D837B5C234C5A8E"
],
"keyAttestation": {
"required": false
},
"openid4vpConfiguration": {
"redirectUri": "com.yourcompany.verifierapp://oid4vp-callback"
}
}
```
* `name`: A unique name to identify this Verifier Application.
* `type`: Must be `android`.
* `packageName`: The package name of your Android application.
* `packageSigningCertificateThumbprints`: SHA-256 hex-encoded fingerprints of the signing key
certificates used to sign your APK or app bundle. This ensures the tenant only accepts requests
from known and trusted applications. Refer to
[Android app signing](/docs/verification/android-app-signing) for more information.
* `keyAttestation`: Key Attestation configuration for the Android verifier application:
* `required`: When `true`, the app instance must provide a valid Key Attestation during
registration and token renewal. When `false`, the app can register and renew tokens using
just an authentication assertion. See [Attestation vs Assertion](/docs/verification/sdks/sdk-tethering#attestation-vs-assertion-fall-back) for more details.
* `openid4vpConfiguration.redirectUri`: Required by the create-application endpoint, which needs at
least one of `openid4vpConfiguration` or `dcApiConfiguration`. In-person proximity verification
does not use this redirect, so any valid custom-scheme URI is accepted here.
A successful response returns a `201` status code with the created Verifier Application:
```json title="Response"
{
"id": "a82bfa46-72a0-4cde-b6cb-2a0de7e2f3c4", // [!code focus]
"name": "My Android Verifier Application",
"type": "android",
"packageName": "com.yourcompany.verifierapp",
"packageSigningCertificateThumbprints": [
"1232584B6F6A892D356899FB9576C5F226A179E6199F2B7A1D837B5C234C5A8E"
],
"keyAttestation": {
"required": false
}
}
```
* `id`: A unique identifier for the Verifier Application (generated by the tenant). You must use this value when initializing the SDK so that it can correctly
identify and authenticate your application.
SDK Tethering is currently not required for the React Native Verifier SDK.
### Initialize the SDK with platform configuration [#initialize-the-sdk-with-platform-configuration]
When you initialize the SDK, you must provide a `PlatformConfiguration` object with your tenant host and the `id` of the Verifier Application you created. This allows the SDK to register the app instance with your tenant and obtain a license to operate.
Initialize the SDK with your platform configuration. The `initialize` method is asynchronous, so call it from an asynchronous context:
```swift title="Initialization"
let platformConfig = PlatformConfiguration(
tenantHost: URL(string: "https://your-tenant.vii.mattr.global")!,
applicationId: "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
)
try await MobileCredentialVerifier.shared.initialize(
platformConfiguration: platformConfig
)
```
* `tenantHost`: The URL of your MATTR VII tenant. This must be the tenant where your iOS Verifier
Application is configured.
* `applicationId`: The `id` of your configured iOS Verifier Application.
Initialize the SDK with your platform configuration:
```kotlin title="Initialization"
val platformConfig = PlatformConfiguration(
tenantHost = URL("https://your-tenant.vii.mattr.global"),
applicationId = "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
)
MobileCredentialVerifier.initialize(context, platformConfig)
```
* `tenantHost`: The URL of your MATTR VII tenant where your Android Verifier Application is configured.
* `applicationId`: The `id` of your configured Android Verifier Application.
SDK Tethering is currently not required for the React Native Verifier SDK.
Once your Verifier Application configurations are created, your application will be able to use the SDK and
interact with the MATTR VII platform (for example, to verify credential presentations).
## Managing application instances [#managing-application-instances]
Once your Verifier Application is configured and the SDK is initialized, each device that launches
your app registers as a new application instance on your tethered MATTR VII tenant. You can view and manage
these instances via the MATTR VII API.
### Retrieve all registered instances [#retrieve-all-registered-instances]
To view all registered instances for a Verifier Application and track usage:
```http title="Request"
GET /v2/presentations/applications/{applicationId}/instances
```
* `applicationId` : The `id` of the Verifier Application you want to inspect.
The response includes a paginated list of all registered instances:
```json title="Response"
{
"data": [
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c",
"appAttestationType": "app_attestation",
"registeredAt": "2023-10-05T14:48:00.000Z",
"licenseExpiresAt": "2024-10-05T14:48:00.000Z",
"lastAttestedAt": "2023-12-01T10:30:00.000Z",
"externalReferenceId": "external-ref-12345",
"deviceDetails": {
"deviceModel": "iPhone 12",
"deviceMake": "Apple",
"osVersion": "iOS 14.4"
},
"sdkDetails": {
"sdkVersion": "1.2.3"
}
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
Each instance includes:
* `id` : Unique identifier for the registered instance.
* `appAttestationType` : The type of attestation used during registration (`none`,
`app_attestation`, or `key_attestation`).
* `registeredAt` : When the instance was first registered.
* `licenseExpiresAt` : When the instance's license expires (the Verifier SDK will automatically handle license renewal).
* `lastAttestedAt` : When the instance was last attested.
* `deviceDetails` : Information about the device (model, make, OS version).
* `sdkDetails` : Information about the SDK version used by the instance.
This is useful for tracking how many devices are actively using your application and monitoring usage quotas.
### Delete a specific instance [#delete-a-specific-instance]
To remove a specific registered instance:
```http title="Request"
DELETE /v2/presentations/applications/{applicationId}/instances/{instanceId}
```
* `applicationId` : The `id` of the Verifier Application.
* `instanceId` : The `id` of the specific instance to delete.
Once deleted, the instance can no longer interact with the platform or receive tokens, and any
existing tokens are revoked.
Deleting instances is primarily useful during **testing** when you have a limited number of devices
and need to re-register a fresh instance (for example, to test the initial registration flow again).
In production, there is nothing preventing the application from requesting another token on the next
launch, which would create a new instance — so deleting instances is not an effective way to block
a device.
## Attestation vs Assertion fall-back [#attestation-vs-assertion-fall-back]
When configuring a Verifier Application, you control whether your MATTR VII tenant requires
**attestation** (hardware-backed proof of app integrity) or also accepts a lighter-weight
**assertion** (a cryptographic signature proving key possession) during instance registration and
token renewal.
Each platform has an attestation configuration with a `required` boolean:
* When `required` is `true`, the app instance must provide a valid attestation during registration
and token renewal.
* When `required` is `false`, your tenant also accepts an assertion when an attestation is not
available.
The SDK handles this automatically. It always attempts to provide an attestation, and falls back to
an assertion if it cannot generate one (for example, when the platform attestation service is
temporarily unavailable). Your tenant then accepts or rejects the request based on the `required`
setting. Your application does not need to manage attestation or assertion details directly.
### When to use each setting [#when-to-use-each-setting]
| Scenario | Recommended setting |
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| **Production apps in distribution** | `required: true`: Provides the strongest integrity guarantees by verifying the app and device through OS-level attestation. |
| **Development and testing** | `required: false`: Useful when running on simulators or devices where attestation services are unavailable. |
| **Broad device compatibility** | `required: false`: Some older devices may not support hardware attestation. The assertion fall-back ensures these devices can still register. |
Setting attestation to `required: false` reduces the security guarantees of the tethering
process. Only use this setting when you have a specific need, such as supporting older devices
or during development.
# Preview Terms of Use (archived)
URL: /docs/resources/terms/archive/preview-terms-of-use
> This document is provided for archival purposes only.
Last Updated: 15 September 2020
These terms of use (“**Terms**”) apply to your preview use of the MATTR Platform, as described in
section 1 below (“**Platform**”).
These Terms are agreed between MATTR Limited (“**we**”, “**us**”, or “**our**”) and you or the
entity you represent, as applicable (“**you**” or “**your**”). By clicking the “I Agree” button
presented alongside these Terms, or using any part of the Platform, you are entering into a binding
contract with us for your access to the Platform on the basis of these Terms. If you’re accepting
these terms on behalf of a company, or another legal entity, you represent and warrant to us that
you have the authority to agree and accept these Terms on behalf of that entity.
1. **Platform.** The Platform is a hosted Platform as a Service (“**PaaS**”). We make the Platform
available to you under these Terms as a hosted service. The Platform comprises the following:For
the purpose of this subsection, associated application programming interface (“**APIs**”) refers
to the endpoints exposed by your tenant on our platform.
1. **Platform Core.** Platform Core includes the following capabilities and their associated
APIs:
1. Verifiable Credentials;
2. Verifiable Presentations;
3. Decentralised Identifier;
4. Secure Messaging;
2. **Platform Extensions**
3. **Platform Drivers**
4. **Other auxiliary capabilities** we may make available from time to time in our discretion
(e.g. reporting and configuration).
5. **Mobile Wallet.** Mobile Wallet is an application for users to store, discover and present
digital credentials.
6. **Other modules and features.** The Platform is being actively developed so we may add new
modules and features during the Term. If we make new modules and features available to you,
these Terms will apply to those modules and features.
7. **Materials.** We may make available from time to time in our discretion certain software
(“**Software**”), documentation and other materials relating to the Platform (together, the
“**Materials**”), including:
1. MATTR Learn assets and documentation;
2. system documentation, API specifications, integration guides, system architecture,
troubleshooting, installation, configuration manuals and other technical documentation;
3. user documentation in the form of onboarding, user guides, tutorials and operational or
process guides;
4. testing, integration testing and prototyping environments; and
5. supporting developer information such as Software Development Kit (SDK), source code
examples, supporting operational tools or frameworks.
2. **Usage limitations.** You are only permitted to use the Platform on a preview basis and for a
limited time (and functionality may be limited). Access to the Platform under these Terms is
provided free of charge, subject to your compliance with the following usage limitations:
1. you may issue or verify a combined maximum of 200 credentials per calendar month;
2. we’ll assign you with non-customisable domains only; and
3. you can only use non-production Decentralised Identifiers (“**DIDs**”) that don’t include
personal information of identifiable individuals.If you are interested in production usage,
please contact us to learn more about our Enterprise Plan.
3. **Use of the Platform.**
1. **Licence and use.** During the Term, subject to your compliance with these Terms, and for
the sole purpose of your internal testing and evaluation:
1. we grant you a non-exclusive, non-transferable licence to use the Materials (including
any Software in machine-executable object code form only); and
2. you may access and use the Platform.
2. **Restrictions and limitations.** You may use the Platform only in accordance with the usage
limitations and restrictions described in sections 2 and 4, and you must not use live data
with the Platform or otherwise use the Platform for production purposes.
3. **Use of data.** You are solely responsible for any and all data that you use with the
Platform, including backing up that data.
4. **Restrictions.** You must not:
1. use the Platform in a way prohibited by any applicable laws, rules, regulations, directives,
and binding guidance of any governmental authority in any jurisdiction where you are using
the Platform;
2. use the Platform in any way that:
1. violates others’ rights;
2. gains or attempts to gain unauthorised access to or disrupt any service, device,
account, or network;
3. distributes unsolicited email or malware; or
4. could harm the Platform or impair anyone else’s use of it;
3. copy or modify the Platform, including any Materials except as expressly permitted under
these Terms or with our prior written consent in each instance;
4. reverse assemble, reverse engineer, decompile or otherwise attempt to derive source code
from the Platform;
5. attempt to work around any technical or licensing limitations in the Platform;
6. sublicense, distribute, sell, lend, rent, lease, transfer, reexport, or grant any rights in
or to all or any portion of the Platform;
7. publish or otherwise disclose to any third party any results of any benchmark or other
performance tests of the Platform; or
8. remove, alter, or obscure any proprietary rights notices contained in or affixed to the
Platform.
5. **Platform updates.** We may make updates to the Platform from time to time, including adding or
removing functionality. We reserve the right to make breaking changes to the platform without
notification and we will not be responsible for fixing them.
6. **Service levels and support.** Because we are providing you with preview access to the
Platform:
1. no Service Level Agreements (“**SLAs**”) apply;
2. the Platform may experience interruptions and downtime (during with you may be unable to
access data or functionality); and
3. we will have no obligation to provide support services for the Platform.
7. **Ownership and rights.**
1. **The Platform.** We or our licensors (not you) own and retain all rights, title and
interest in and to the Platform (including any additions, modifications and derivative
works). We may at any time and at our sole election replace, modify, alter, improve,
enhance, or change any part of the Platform. Except for your limited access and use rights
expressly set out in these Terms, we reserve all rights in and to the Platform. We don’t
grant any additional rights and you are responsible for obtaining any licences to use
related or enabling technologies that may be required for you to access and use the
Platform.
2. **Third-party materials.** The Platform may include third-party software and materials,
which may be licensed or made available subject to additional or alternative terms and
conditions.
8. **Term.** These Terms take effect when you click the “I Agree” button presented alongside these
Terms or when you first use any part of the Platform, whichever is earliest (“**Effective
Date**”) and continue for three months unless earlier cancelled in accordance with section 9 or
extended by us at our discretion (“**Term**”).
9. **Suspension and cancellation.** We may suspend or cancel your access to the Platform
immediately by written notice and without liability to you if you:
1. breach or otherwise fail to comply with these Terms; or
2. we determine in our sole discretion that such action is reasonably necessary to avoid
liability to any person or to ensure compliance with any applicable laws.
If we cancel your access to the Platform, this will be considered termination for the purposes
of section 8.
10. **Consequences of termination or expiry.** When the Term ends for any reason, you must:
11. immediately stop using the Platform; and
12. within 10 days, return or destroy all copies of the Materials in your possession or control.
13. **Survival.** The end of the Term won’t limit or impact:
14. any rights of a party that have accrued up to and including date on which the Term ends; or
15. sections 4 (Restrictions), 5 (Ownership and rights), 8 (Confidential Information), 9
(Warranties), 10 (Liability) and 11 (Miscellaneous), which survive expiry or termination.
16. **Feedback.** You will promptly notify us by email of any actual or potential error or bug in
the Platform. We may, in our sole discretion, elect to correct any errors or bugs confirmed by
us to exist in the Platform. You may submit comments, suggestions, feedback, feature requests or
ideas about the Platform (“**Feedback**”). You agree to make no claim of ownership of the
Feedback or any intellectual property rights that may subsist in it, and we are free to use the
Feedback without any additional compensation to you, including disclosing the Feedback to anyone
on a confidential or non-confidential basis. We are free to ignore, incorporate, use, disclose,
reproduce, license, distribute, modify, perform, display, and otherwise exploit any Feedback, at
our sole discretion, without any restriction of any kind (whether due to intellectual property
rights or otherwise) and without payment to you. You may only provide Feedback to us that you
have the right to provide.
17. **Confidential Information.**
18. **Definition of Confidential Information.** In these Terms, “**Confidential Information**” means
any information disclosed by one party (“**Disclosing Party**”) to the other (“**Receiving
Party**”), whether before or after the Effective Date, that:
1. is in written, graphic, machine readable or other tangible form and is marked
“Confidential”, “Proprietary” or in some other manner to indicate its confidential nature;
2. should be reasonably understood by Receiving Party to be the confidential or proprietary
information of Disclosing Party; or
3. that is oral information disclosed by Disclosing Party to Receiving Party, provided that
such information is designated as confidential at the time of disclosure and is reduced to
writing by Disclosing Party within a reasonable time after its oral disclosure, and such
writing is marked in a manner to indicate its confidential nature and delivered to Receiving
Party. For clarity, the Platform is our Confidential Information.
19. **Obligation of confidentiality.** Subject to section 12, Receiving Party must:
1. treat as confidential all Confidential Information of Disclosing Party;
2. not use such Confidential Information except to exercise its rights and perform its
obligations under these Terms,
3. not disclose such Confidential Information to any third party
4. use at least the same degree of care it uses to prevent the disclosure of its own
confidential information of like importance, to prevent the disclosure of Confidential
Information of Disclosing Party.
5. promptly notify Disclosing Party of any actual or suspected misuse or unauthorised
disclosure of Disclosing Party’s Confidential Information.
20. **Exception.** Each party’s obligation to maintain confidentiality shall not apply to any
information that:
1. is, or becomes, available to the public through no fault or breach by the Receiving Party;
2. was in the Receiving Party’s possession prior to disclosure by the Disclosing Party;
3. is disclosed to the Receiving Party by a third party having, to the best of the Receiving
Party’s knowledge, the right to make such disclosure; or
4. is independently developed by the Receiving Party without reference to the Disclosing
Party’s Confidential Information.
21. **WARRANTIES.**
22. THE PLATFORM IS MADE AVAILABLE BY US ON AN “AS IS”, “WITH ALL FAULTS” AND “AS AVAILABLE” BASIS.
TO THE MAXIMUM EXTENT PERMITTED BY LAW, WE MAKE NO WARRANTIES, EXPRESS, IMPLIED, STATUTORY OR
OTHERWISE WITH RESPECT TO THE PLATFORM OR THE USE OR OPERATION THEREOF, AND SPECIFICALLY
DISCLAIM THE IMPLIED WARRANTIES OF MERCHANTABILITY, ACCURACY, FITNESS FOR A PARTICULAR PURPOSE
AND NON-INFRINGEMENT. WE DO NOT WARRANT THAT THE PLATFORM WILL FUNCTION AS DESCRIBED OR BE
ERROR-FREE, SECURE, RELIABLE OR ACCURATE. NO ORAL OR WRITTEN STATEMENT MADE TO YOU IN RELATION
TO THE PLATFORM SHALL CREATE ANY WARRANTY THAT HAS BEEN EXPRESSLY DISCLAIMED IN THESE TERMS.
23. WE ASSUME NO RISK OR LIABILITY IN YOUR USE OF THE PLATFORM. YOU ARE SOLELY RESPONSIBLE FOR
DETERMINING THE APPROPRIATENESS OF USING THE PLATFORM AND ASSUME ALL RISKS ASSOCIATED WITH USING
THIS VERSION OF THE PLATFORM, INCLUDING RISKS AND COSTS OF PLATFORM ERRORS, COMPLIANCE WITH
APPLICABLE LAWS, DAMAGE TO OR LOSS OF DATA, PROGRAMS OR EQUIPMENT, AND UNAVAILABILITY OR
INTERRUPTION OF OPERATIONS.
24. **LIABILITY.** TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT WILL WE BE LIABLE FOR ANY
LOSSES, LIABILITIES, DAMAGES OR EXPENSES, INCLUDING LOSS OF DATA, LOSS OF SYSTEM AVAILABILITY,
LOSS OF COMPUTER RUN TIME, BUSINESS INTERRUPTION, LOST PROFITS, LOST REVENUE, LOST GOODWILL,
LOST BUSINESS, ANTICIPATED SAVINGS, COST OF COVER OR OTHER SPECIAL, INCIDENTAL, CONSEQUENTIAL,
DIRECT OR INDIRECT DAMAGES ARISING FROM THE USE OF THE PLATFORM OR ACCOMPANYING MATERIALS,
HOWEVER CAUSED AND WHETHER BASED IN CONTRACT, TORT (INCLUDING NEGLIGENCE) OR ANY OTHER THEORY OF
LIABILITY. THIS LIMITATION WILL APPLY EVEN IF WE HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES. THE PARTIES ACKNOWLEDGE THAT THIS IS A REASONABLE ALLOCATION OF RISK.
25. **Indemnity.** You will defend, hold harmless and indemnify us from and against any claim or
action brought by a third party (including all damages, liabilities, costs and expenses, and
lawyers’ fees on a solicitor and own-client basis) arising out of or in connection with your
breach of these Terms or infringement of our intellectual property.
26. **Miscellaneous.** You may not assign, sublicense or otherwise transfer the rights or licence
granted under these Terms, by agreement or by operation of law, without our prior written
consent, and all assignments in violation of this prohibition will be null and void. These Terms
amount to the entire agreement between you and us relating to its subject matter. This Agreement
will be governed by the laws of New Zealand without reference to conflict of law principles. In
any dispute arising out of this Agreement, you consent to the jurisdiction of New Zealand courts
and agree to bring any actions arising out of these Terms in such court. Any delay or failure by
us to exercise a right or remedy will not result in a waiver of that, or any other, right or
remedy. Where the term “include” or its grammatical variations appear, this does not limit the
text that follows. If any part of these Terms is held unenforceable, the remainder of these
Terms will continue in full force and effect.
27. **Updates.** We may update or replace these Terms at any time by publishing a new version on our
website. By using the Platform after the “Last updated” date at the top of these Terms, you are
deemed to have accepted the updated Terms. If you don’t agree to the updated Terms, you must
stop using the Platform immediately.
# Privacy Policy (archived)
URL: /docs/resources/terms/archive/website-privacy-policy-legacy
> This document is provided for archival purposes only.
Last Updated: 30 November 2020
MATTR is built and operated as a privacy-first company. Enhancing privacy and trust in digital
transactions is part of our DNA. This privacy policy explains how we collect, use and disclose data
that relates to you, and applies to your use of our Website and the Preview Platform, (collectively,
the **Services**). We have a separate privacy policy for the Preview Digital Wallet, which is
currently available by invite only.
When we say “**our**”, “**we**”, or “**us**”, we mean MATTR Limited (**MATTR**). When we say
“**you**” or “**your**” we mean you or the entity you represent, as applicable. By using our
Services, you acknowledge that you have read and accepted our privacy policy.
We may update this privacy policy from time to time by publishing the updated version on our
Website. We will update the “Last Updated” date at the top of this page, and your use of our
Services after that date confirms your acceptance of the updated policy.
As a New Zealand-based company, we comply with the New Zealand Privacy Act (**Act**) when dealing
with your information. This policy does not limit or exclude any of your rights under the Act. You
can find more information about the Act and your rights at
[www.privacy.org.nz](https://privacy.org.nz/).
#### How we collect personal data [#how-we-collect-personal-data]
We only ask for your information where we actually need it. When you use our Services, we ask you to
provide certain personally identifiable information **(Personal Data)** which we may use to identify
and contact you. Examples of Personal Data we ask for when you use certain aspects of the Services
(e.g. if you sign up to use our Preview Services) include your name, email address, and
organisation. In situations where you can’t be identified (for example, because the information is
aggregated and anonymised) then that is not Personal Data.
#### How we use the personal data we collect [#how-we-use-the-personal-data-we-collect]
We use the Personal Data that we collect when you use our Services to:
* provide, update and maintain the Services,
* secure, troubleshoot, and provide support,
* notify you about changes,
* respond to you if you make an inquiry,
* if you opt in, to send you materials and updates about our business,
* analyse, improve and develop the functionality of our Services,
* prevent, detect, or investigate security concerns, including fraud,
* comply with applicable laws, regulations, and industry requirements, and
* if not captured above, fulfil a purpose that you have expressly requested from us or consented
to.
#### Disclosure of your personal data [#disclosure-of-your-personal-data]
We ***do not*** sell your personal data or share it without your consent. We will only disclose your
Personal Data:
* if required to comply with applicable laws, regulations, or legal processes,
* to prevent, detect, or investigate security concerns, including fraud
* to the extent it is necessary, to partners that we engage to provide aspects of the Services.
#### Protecting your personal information [#protecting-your-personal-information]
We will take reasonable steps to protect your Personal Data from loss, and unauthorised disclosure
and use. If a privacy breach occurs, we will endeavour to communicate with you and all relevant
parties as soon as possible, and report all serious incidents to the Privacy Commissioner. We will
take appropriate steps to minimise the harm of the incident.
#### Accessing and correcting your personal information [#accessing-and-correcting-your-personal-information]
You are entitled to access the Personal Data that we hold (provided it is readily retrievable) and
to request a correction. You may also ask us to confirm whether we hold particular information.
These rights may be subject to certain conditions or grounds for refusal, as set out in the Act.
If you want to exercise any of the above rights, please email us at
[privacy@mattr.global](mailto:privacy@mattr.global). You will need to confirm your identity and set
out the details of your request (eg. the Personal Data you would like to access, or the correction
you are requesting).
#### How long we keep your personal information [#how-long-we-keep-your-personal-information]
We will retain your Personal Data only for as long as is necessary (for the purposes for which it
was collected) and as otherwise required by law.
#### Cookies [#cookies]
A cookie is a small text file that websites can ask your browser to store on your device to enhance
the customer experience. We use a small number of cookies for the purpose of storing preferences. We
do not use any cookies for the purposes of marketing.
You can disable cookies by changing the settings on your browser, which may impact your experience
of our Website.
#### Preventing bots and spam [#preventing-bots-and-spam]
Our Website is protected by the hCaptcha anti-bot service. This checks whether the data entered on
our Website (such as on our sign-up form for Preview Services) has been entered by a human or by an
automated program. To learn more you can read hCaptcha’s
[privacy policy](https://www.hcaptcha.com/privacy) and
[terms of service](https://www.hcaptcha.com/privacy).
#### Privacy-focused analytics [#privacy-focused-analytics]
We do not use Google Analytics. We’ve made the conscious choice to pay for a privacy-first analytics
tool called Fathom. You can learn more about privacy-focused analytics
[here](https://usefathom.com/privacy-focused-web-analytics), and read Fathom’s privacy policy
[here](https://usefathom.com/privacy).
#### Security of Data [#security-of-data]
The security of your data is very important to us – but please remember that no method of
transmission over the Internet or method of electronic storage is 100% secure. While we strive to
use commercially acceptable means to protect your Personal Data, we cannot guarantee its absolute
security.
#### Links to other websites [#links-to-other-websites]
If you follow a link from our Website to a website hosted by a different entity, that website will
be subject to a different privacy policy and terms of service, which we recommend that you review.
#### Privacy queries [#privacy-queries]
If you have any queries about this privacy policy, you can contact us at any time at
[privacy@mattr.global](mailto:privacy@mattr.global).
# MATTR Website Terms of Use (archived)
URL: /docs/resources/terms/archive/website-terms-of-use-legacy
> This document is provided for archival purposes only.
Last Updated: 15 September 2020
Welcome to the MATTR website!
These Website Terms of Use (“**Terms**“) are agreed between MATTR Limited (“**we**”, “**us**”, or
“**our**”) and you or the entity you represent, as applicable (“**you**” or “**your**”) and apply
only to your use of our Website. Different terms apply to your use of our Services (“**Services**“).
By using our Website, you acknowledge that you have read and accepted these Terms. We may update or
replace these Terms from time to time by publishing a new version on our Website. By using the
Website after the “Last updated” date at the top of these Terms, you are deemed to have accepted the
updated Terms. If you don’t agree to the updated Terms, you must stop using the Website.
#### Your use of our Website [#your-use-of-our-website]
The information provided in our Website is intended only as an introduction and guide to our range
of available Services.
You must not use our Website or its contents:
* in breach of any law or for any unlawful act, or
* to damage or disrupt our Website, Services or any other service or website
#### Liability [#liability]
To the maximum extent permitted by law:
* our Website and the information presented on it is provided on an “as-is” and “as-available”
basis without any warranties, representations, or guarantees of any kind (whether, express,
implied, statutory or otherwise) including, but not limited to, warranties of non-infringement,
merchantability, or fitness for a particular purpose, and
* neither we nor our suppliers will be liable under the law of tort (including negligence),
contract or otherwise for any loss of income, profits, data, business opportunity or savings or
for any indirect, incidental, consequential, exemplary, punitive or special loss or damage of
any person, however caused, arising out of or in connection with your use of our Website or
reliance on any information on our Website.
#### Intellectual property [#intellectual-property]
We or our licensors (as applicable) own all copyright and all other intellectual property rights
that may subsist in this Website or any content presented on this Website (and we reserve all rights
in relation to such material). You may use this Website for non-commercial purposes only, and you
must:
* not remove any copyright, trademark and other proprietary notices contained in the content
* not copy, display, redistribute or otherwise use for commercial purposes any portion of this
site without our prior written permission, and
* include an attribution to MATTR Limited if you use any information contained on our Website
The trademarks appearing on our Website belong to us, our suppliers, or our licensors as applicable.
You must not use or reproduce or allow anyone to use or reproduce those trademarks for any reason
without prior written permission.
#### Feedback and unsolicited submissions [#feedback-and-unsolicited-submissions]
If you give us feedback or submit ideas about our Website or Services (**“Ideas”**), you:
* grant us the right to use those Ideas to improve our Website and our products and services (and
for any other purpose we deem necessary or desirable) without being obliged to seek your
permission or pay you any compensation in respect of our use of those Ideas, and
* waive any moral rights in those Ideas and consent to us freely using those Ideas without further
consent or attribution to you.
#### Third party sites [#third-party-sites]
Our Website may include links to external sites. These sites are not under our control and we are
not responsible for, and we make no representations or warranties concerning the contents of any
such external site. Such links are provided to you only as a convenience, and the inclusion of any
link does not imply endorsement, verification, or certification by us of the linked site.
#### Jurisdiction [#jurisdiction]
Our Website and these Terms are governed by the laws of New Zealand and the courts of New Zealand
will have non-exclusive jurisdiction to hear and determine any dispute arising in relation to them.
# MATTR Customer Agreement (archived)
URL: /docs/resources/terms/customer-agreement/20-4-22
This document is provided for archival purposes only.
Last Updated: 20 April 2022
[See what's changed](/docs/resources/terms/customer-agreement/recent-changes) |
[Previous versions](#previous-versions)
This Agreement sets out the terms and conditions governing your access to and use of the Services
and Materials (as defined below). It is an agreement between MATTR Limited (**we**, **us**, or
**our**) and you or the entity you represent (**you** or **your**).
This Agreement takes effect when you click an “I Accept” button or tick box presented alongside
these terms, or when you otherwise access or use any of the Services or Materials (**Effective
Date**).
By entering into this Agreement, you warrant and represent that:
1. you are lawfully able to enter into contracts (e.g. you are not a minor); and
2. if you are entering into this Agreement for an entity (e.g. a company, government agency or
other organisation), you are authorised to do so and have legal authority to bind that entity.
1) #### Use of the Services and Materials. [#use-of-the-services-and-materials]
1. **General.** You may access and use the Services and Materials only in accordance with this
Agreement. As part of this Agreement, Service Level Agreements (**SLAs**) and Service Terms
apply to certain Services. Service Terms may specify additional Fees, restrictions and
obligations in connection with such Services. You must comply with the terms of this
Agreement and all Laws applicable to your use of the Services and Materials.
2. **Your account.** To access the Services, you must have an Account associated with a valid
email address and a valid form of payment.
3. **Third-Party Components.** You may use Third-Party Components that are made available to you
on the Site or through the Services. Third-Party Components are governed by this Agreement
and any separate terms and conditions accompanying such Third-Party Components (which may
include separate fees and charges).
4. **Trial period.** We may supply Services on a trial basis. Your Account may be suspended at
the end of the trial period unless you upgrade your Account to continue using or accessing
the Services.
2) #### Your Systems and Third-Party Components. [#your-systems-and-third-party-components]
1. **Your Systems.** We may need you to provide us with access to any interfaces, software,
platforms, systems or infrastructure that are not owned or operated by us in order to supply
a Service to you (**Your Systems**). For each of Your Systems:
1. it is a condition of our supply of the Service that you obtain the necessary access for
us on an ongoing basis (including permissions and credentials) for as long as required by
us;
2. we will undertake that access on your behalf. You appoint us as your agent for this
purpose and consent to us representing to third parties that we are your agent for this
purpose;
3. if any charges or fees apply for that access, we may require you to pay those charges and
fees before we supply the Service, or we may pay those charges and fees and recover them
from you as Fees (with a reasonable administrative charge if specified in the relevant
Service Terms); and
4. you will defend, hold harmless and indemnify us from and against any claim or action
brought by a third party, including all damages, liabilities, costs and expenses, and
lawyers’ fees on a solicitor and own-client basis) arising out of or in connection with
our use of or access to Your System in accordance with this Agreement.
2. **Third-Party Components**
1. You acknowledge that some Services that we provide are developed or provided by third
parties and not by us (**Third-Party Components**).
2. Certain Third-Party Components we provide are subject to additional Service Terms, and
may be subject to a third-party licence. As set out in the Service Terms, these are
provided on an ‘as-is’ basis without any warranty of any kind. If there is any conflict
between this Agreement and that separate licence, the separate licence will prevail with
respect of the Third-Party Components.
3. We, not the Third-Party Components provider, are responsible for the supply. The
Third-Party Components provider and its licensors provide no warranties, support or
indemnities in respect of the Third-Party Components.
3) #### Changes. [#changes]
1. **Services.** We may change or discontinue any of the Services from time to time, subject to
any SLA we have agreed with you.
2. **Critical changes.** We may make changes referred to in clause 3.1 without notice and
despite any SLA to:
1. address or avoid a security or Intellectual Property risk to us or the Services or
Materials;
2. avoid any violation of any Law; or
3. maintain the commercial viability, security and availability of the Services or
Materials.
3. **Service Level Agreements.** We may change, discontinue or add Service Level Agreements from
time to time in accordance with clause 17.
4) #### Data security and storage. [#data-security-and-storage]
1. **Data security.** Without limiting clause 15 or your obligations under clause 6, we will
implement reasonable measures designed to help you secure Your Content against unauthorised
access, interference, modification, loss, or disclosure.
2. **Data region.** Where we allow you to select from a number of available Regions, you may
choose the Region in which Your Content will be processed and stored as part of the Services.
You consent to the storage of Your Content in, and transfer of Your Content into, the Region
you have selected. To provide billing, administration and support services, we may use and
process your Account Information in the Region you have selected and in another Region where
our billing, administration and support services are located.
3. **Data access.** We will not access, use or disclose Your Content except as is necessary or
required to:
1. maintain or provide the Services and Materials;
2. comply with the Law or legal processes, or to exercise, establish or defend our legal
rights; or
3. mitigate potential negative implications of a disaster or security incident (as
determined by us) which may include transfer of your data to a different Region.
4. **Legally requested disclosure.** We will not disclose Your Content or Account Information
except as necessary to comply with the Law or legal processes, or to exercise, establish or
defend our legal rights. If we are allowed to, we will notify you of any request or proposal
to disclose your information.
5) #### Data use and privacy. [#data-use-and-privacy]
1. **Use of Data.** We respect you and your End Users’ privacy and take data protection
seriously. We will only use your Account Information in accordance with our Privacy Policy,
which describes in more detail how we deal with Account Information and the personal data of
End Users. You must ensure that you comply with all applicable data protection and privacy
laws, including by obtaining the consent of each End User for the collection, use, storage,
transfer, processing and disclosure of personal information, sensitive information or special
category data (as those terms are defined in applicable data protection and privacy laws).
2. **Data Processing Terms.** Depending on the location of your End Users and other individuals
whose personal information will be collected, used, stored, transferred, processed or
disclosed as a result of you using or accessing the Services or Materials, our
[Data Processing Terms](/docs/resources/terms/data-processing-terms) may apply.
3. **Anonymised statistical data.** When you use our Services, we may create anonymised
statistical data from your data and usage of our Services, including through aggregation.
Once anonymised, we may use it for our own purposes, such as to provide and improve our
Services, to identify any unacceptable use of our Services, to develop new services or
product offerings, to identify business trends, and for other uses we communicate to you.
4. **Data breach notifications.** If we think there has been unauthorised access to, or
disclosure of, personal information inside your Account, we will let you know and endeavour
to give you information about what happened. Depending on the nature of the unauthorised
access or disclosure, and the location of those affected, you may be required to assess
whether the unauthorised access or disclosure must be reported to those affected and/or a
relevant authority. We will rely on you to make this decision, because you will have the most
knowledge about the personal information stored in your Account.
6) #### Your responsibilities. [#your-responsibilities]
1. **Your accounts.** Except to the extent caused by our breach of this Agreement:
1. you are responsible for all activities that occur under your Account, regardless of
whether the activities are authorised by you or undertaken by you, your employees or a
third party (including your contractors, agents or End Users); and
2. we and our related companies are not responsible for unauthorised access, interference or
modification to your Account, or any unauthorised access, modification, loss or
disclosure of Your Content or personal information or other data in your Account.
2. **Your Content and use of the Services.** You must ensure that Your Content, your, and your
End Users’ use of Your Content, Services and Materials comply with this Agreement, all
applicable Laws and all restrictions described in the MATTR Content and on the Site, and any
other policy or terms referenced in or incorporated into this Agreement. You are solely
responsible for the development, content, operation, maintenance, and use of Your Content.
3. **Securing and protecting Your Content.** Except to the extent we have agreed in writing or
expressly committed offered as part of the Services, you are responsible for:
1. properly configuring and using the Services and Materials; and
2. taking reasonable and appropriate action to secure, protect and backup your Account and
Your Content in a manner that will provide appropriate security and protection (which
might include use of encryption to protect Your Content) from unauthorised access,
interference, modification, loss or disclosure and routinely archiving Your Content.
4. **Account log-In details and private keys.** Account log-in details and private keys
generated by the Services are for your internal use only. You are responsible for protecting
them against unauthorised use or disclosure and you must not sell, transfer or sublicense
them, except:
1. to the extent that we otherwise permit in writing (which we may permit subject to
conditions); and
2. that you may disclose your private key to your employees, agents and contractors
performing work on your behalf.
5. **End Users.** You are responsible for your End Users’ use of Your Content, the Services and
the Materials. You must ensure all End Users comply with the terms of this Agreement and
agree to terms that are consistent with this Agreement. We do not have to provide any support
or services to End Users unless we have a separate agreement with you or them for the
provision of such support or services or are required to support them by Law (e.g. with
access to personal information).
7) #### Unacceptable use. [#unacceptable-use]
1. **Overview.** You must not, and must ensure that End Users will not, use the Services or
Materials in any manner or for any purpose other than as expressly permitted by this
Agreement. The examples described in this clause are not exhaustive. If you do not comply,
and ensure your End Users comply, with this clause, we may suspend or terminate your access
to and use of the Services in accordance with clauses 10 or 12 as appropriate.
2. **Illegal, harmful, or offensive use or content.** You must not use, or encourage, promote,
facilitate or instruct others to use, the Services or Materials in a manner that is illegal,
harmful, fraudulent, offensive or that infringes on the right of any person, or to transmit,
store, display, distribute or otherwise make available content that we consider to be
illegal, harmful, fraudulent, or offensive.
3. **Malware.** You must not use the Services or Materials to host or distribute any Content or
other computer technology that may damage, interfere with, surreptitiously intercept, or
expropriate any system, program, or data (including viruses, Trojan horses, worms, time
bombs, cancelbots and other malware).
4. **Unauthorised access.** You must not access or use the Services or Materials to threaten,
attempt to, or engage in conduct that would violate the security or integrity of (including
though a malicious act), or otherwise gain unauthorised access to, any communication,
network, computer or communications system, software application, or network or computing
device.
5. **Network interference.** You must not threaten, attempt, or engage in, conduct that is
likely to interfere with, pose a security risk to, or adversely impact our systems, Services,
Materials or Site, including the Content of our customers and their use of our Services,
Materials or Site. For clarity, this includes making network connections to any computer
system or network (unless you have permission to do so), monitoring or crawling systems or
networks in a way that impairs or disrupts them, denial of service attacks, intentionally
interfering with any computer system or network, or using any means to avoid usage
limitations and restrictions placed on the Services, Materials, Site or any other computer
system or network.
6. **License restrictions.** You must not breach the licence restrictions set out in clause
13.4.
7. **Monitoring and enforcement.** We may, but are not obliged to, investigate any violation of
this clause or any other misuse of the Services or Materials. We may report any activity that
we suspect violates any Law to law enforcement officials, regulators, or other appropriate
third parties. Our reporting may include disclosing Account Information. We also may
cooperate with law enforcement agencies, regulators, or other appropriate third parties to
help with the investigation and prosecution of illegal conduct by providing network and
systems information related to alleged violations of this clause.
8) #### Fees and payment. [#fees-and-payment]
1. **Fees.** We calculate and charge or invoice Fees Monthly or as otherwise agreed. You must
pay us the Fees for use of the Services and Materials by the due date for payment and using
one of the payment methods we have approved for your Account. You must pay all Fees payable
under this Agreement without any set-off, counterclaim, deduction or other withholding. We
may charge you interest at the Overdue Interest Rate on unpaid Fees from the date such unpaid
Fees became due until the date payment is received into our account in cleared funds.
However, if the Overdue Interest Rate exceeds the maximum permitted legal interest rate, the
interest chargeable will be reduced to reflect the maximum permitted legal interest rate.
2. **Changes to Fees.** Fees and charges for new Services, Materials or service features will be
effective when we post updated Fees on the Site or notify you of the Fees, or such other date
as we expressly state. We may increase or add new Fees for existing Services and Materials at
any time by giving you at least 30 days’ prior notice.
9) #### Taxes. [#taxes]
1. **Fees exclusive of Taxes.** Our Fees exclude Goods and Services Taxes and all other Taxes.
If we are liable for any Goods and Services Taxes or any other Tax in respect of the
provision of the Services or Materials or any other supply we make under this Agreement, you
will pay us, in addition to and at the same as our Fees, an amount equal to the amount of
such Tax, subject to receipt of a tax invoice (if applicable).
2. **Withholding Tax.** If:
1. you are required by Law to make any deduction or withholding from any amount payable to
us under this Agreement; or
2. we are required by Law to pay any Tax in relation to any amount receivable by us under
this Agreement, you must pay to us such additional amounts as are necessary so that,
after making the deduction, withholding or payment, the net amount received and retained
by us is equal to the amount we would have received and retained had no such deduction,
withholding or payment been made.
3. **Information.** If you deduct or withhold any amount from a payment to us under clause 9.2,
you will, at our request, provide us with reasonable evidence of payment of the deduction or
withholding to the relevant tax authority.
10) #### Suspension. [#suspension]
1. **General.** We may suspend your right to access or use any or all Services and Materials
immediately upon notice to you to the extent that we determine:
1. your or your End User’s use of the Services or Materials: (i) is in breach of clauses 5,
6, or 7; (ii) could subject us, our related companies, or any third party to liability;
(iii) could be fraudulent; or (iv) is prohibited under clause 19.6;
2. you are in breach of this Agreement;
3. you are in breach of your payment obligations under clause 8 in respect of any Service or
Materials; or
4. you have ceased to operate in the ordinary course of business, made an assignment for the
benefit of creditors or similar disposition of your assets, or become the subject of any
insolvency, bankruptcy, reorganisation, liquidation, dissolution or similar proceeding.
2. **Effect of suspension.** If we suspend your right to access or use any or all Services or
Materials:
1. you remain responsible for all Fees you incur during the period of suspension; and
2. you will not be entitled to any Service Credits under the Service Level Agreements for
any period of suspension.
11) #### Term. [#term]
1. **Term.** This Agreement will start on the Effective Date and will remain in effect unless
terminated under clause 12.
12) #### Termination. [#termination]
1. **Termination without cause.** Unless we have agreed a committed contract term with you, you
can terminate this Agreement in respect of any or all Services and Materials for any reason
by closing your account for the relevant Services. To terminate in respect of all Services
and Materials, you must close your account for all Services for which we provide an Account
closing mechanism. We may also terminate this Agreement for any or all Services and Materials
for any reason by providing you with at least 30 days’ written notice for any reason except
to the extent we have agreed to a longer committed contract term with you.
2. **Termination for cause.** Either party may terminate this Agreement in respect of any
affected Services and Materials if the other party is in material breach of this Agreement
and the breach remains uncured for a period of 30 days from receipt of written notice by the
other party. You must close your Account for all affected Services for which we provide an
account closing mechanism no later than the date notified to you.
3. **Immediate termination by us.** We may also terminate this Agreement in respect of any or
all Services and Materials immediately upon notice to you:
1. to the extent we have the right to suspend under clause 10;
2. if our relationship with a third-party partner who provides software or other technology
we use to provide affected Services or Materials expires, terminates or requires us to
change the way we provide the software or other technology as part of the Services or
Materials;
3. to comply with any applicable Law or legal processes; or
4. if we are subject to any legal or regulatory changes that we consider make it technically
or commercially burdensome for us to continue providing the affected Services or
Materials to you.
4. **Effect of termination.** Upon the Termination Date:
1. except as provided in clause 12.5, all your rights under this Agreement with respect to
affected Services and Materials immediately terminate;
2. you remain responsible for all Fees you have incurred for affected Services and Materials
up until, and including, the Termination Date and are responsible for any Fees you incur
for those affected Services or Materials during the post-termination period described in
clause 12.5;
3. you will immediately return or, if instructed by us, destroy all Material provided in
connection with affected Services which are in your possession; and
4. clauses 6.1, 8, 12, 13 (except the licence granted to you in clause 13.3), 14, 15, 16,
19, and 20 will continue to apply in connection with the affected Services and Materials.
5. **Consequences of termination.** Unless we terminate your use of the Services or Materials
pursuant to clause 12.2:
1. we will not take action to remove Your Content from the Services as a result of the
termination for 30 days following the Termination Date; and
2. we may remove Your Content from the Services any time following that 30 day period unless
otherwise agreed with you in writing.
6. **Use after Termination Date.** For any use of the Services or Materials after the
Termination Date, your obligations under this Agreement will apply, and you will pay the
applicable Fees in accordance with clause 8.
13) #### Intellectual Property. [#intellectual-property]
1. **Your Content.** You own Your Content. When you transfer Your Content into our Services, you
grant us a non-exclusive, transferable and sublicensable licence to use, copy, communicate,
transmit, store, analyse, adapt and back up all transferred data to provide the Services and
Materials to you and your End Users.
2. **Adequate rights.** You represent and warrant to us that:
1. you or your licensors own all right, title, and interest in and to Your Content and
Feedback, including to Intellectual Property;
2. you have all rights in Your Content and Feedback necessary to grant the rights
contemplated by this Agreement; and
3. none of Your Content or End Users’ use of Your Content or the Services, Materials or Site
will violate clause 5, 6, or 7.
3. **Your licence to use our Services.** We or our licensors own all rights, title, and interest
in and to the Services, Site and Materials, including to Intellectual Property. Subject to
the terms of this Agreement, we grant you a limited, revocable, non-exclusive,
non-transferrable licence for the Term of this Agreement to access and use the Services and
Materials solely in accordance with this Agreement, including subject to clauses 6.2 and 7.
The Services and Materials are not sub-licensable except to the extent set out in applicable
Service Terms or as otherwise notified to you. Except as provided in this clause 13.3, you
obtain no rights under this Agreement from us, our related companies or our licensors to the
Services or Materials, including any Intellectual Property.
4. **Licence restrictions.** You must not, and must not permit End Users to, access or use the
Services or Materials in any manner or for any purpose other than as expressly permitted by
this Agreement. Neither you nor any End User will, or will attempt to:
1. modify, distribute, alter, tamper with, repair, or otherwise create derivative works of
any Content included in the Services or Materials (except to the extent Content is
provided to you under a separate licence that expressly permits the creation of
derivative works);
2. reverse engineer, disassemble, or decompile the Services or Materials or apply any other
process or procedure to derive the source code of any software included in the Services
or Materials (except to the extent applicable Law doesn’t allow this restriction);
3. access or use the Services or Materials in a way intended to avoid incurring fees, bypass
usage limits or bypass quotas; or
4. resell or sublicense the Services or Materials except with our prior written agreement.
[Contact us](mailto:info@mattr.global) to discuss your implementation and requirements.
5. **MATTR Marks.** You may only use the MATTR Marks with our prior written permission and in
accordance with any trademark usage guidelines that we have published on the Site or
otherwise notified to you.
6. **Feedback.** You are not obliged to provide Feedback to us or our related companies. If you
do so:
1. we and our related companies may use, modify or develop the Feedback for any purpose
without restriction and without attribution or compensation to you or any other person;
and
2. you irrevocably assign to us all right, title, and interest in and to the Feedback and
agree to provide us any assistance we require to document, perfect, and maintain our
rights in the Feedback.
14) #### Indemnity. [#indemnity]
1. **General.** You will defend, indemnify, and hold harmless us, our related companies and
licensors, and each of our and their respective employees, officers, directors, and
representatives from and against any third-party claim or Losses arising out of or in
connection with:
1. your or any End Users’ access to or use of the Services or Materials (including any
activities under your Account and use by your employees, agents or contractors);
2. breach of this Agreement or violation of applicable Laws by you, End Users or Your
Content; or
3. a dispute between you and any End User. You will reimburse us for reasonable legal fees,
as well as our employees’, contractors’ and agents’ time and materials spent responding
to any third party subpoena or other compulsory legal order or process associated with
third party claims described in this clause at our then-current hourly rates.
2. **The Services.** Subject to the limitations in this clause 14, we will defend you and your
employees, officers, and directors against any third-party claim alleging that the Services
infringe or misappropriate that third party’s Intellectual Property, and will pay the amount
of any adverse final judgment or any settlement agreed by us.
3. **Your Content.** Subject to the limitations in this clause 14, you will defend us, our
related companies, and our and their respective employees, officers, and directors against
any third-party claim alleging that any of Your Content infringes or misappropriates that
third party’s Intellectual Property, and will pay the amount of any adverse final judgment or
settlement agreed by you.
4. **Exclusions.** We will have no obligations or liability to you under clause 14.2 arising out
of or in connection with:
1. modification of the Services or Materials by anyone other than us or our related
companies;
2. combination, operation or use of the Services or Materials with any other goods,
software, product, data or services not provided by us; or
3. your or any End User’s use of the Services or Materials after we have notified you to
discontinue such use. The remedies provided in this clause 14 are the sole and exclusive
remedies for any third-party claims of infringement or misappropriation of Intellectual
Property by the Services or by Your Content.
5. **Remediation.** For any claim covered by clause 14.2 we will, at our option, either:
1. procure the rights to use that portion of the Services alleged to be infringing;
2. replace the alleged infringing portion of the Services with a non-infringing alternative;
3. modify the alleged infringing portion of the Services to make it non-infringing; or
4. terminate the allegedly infringing portion of the Services or this Agreement or both (as
applicable).
6. **Process.** The party seeking defence or indemnity under this clause 14 must:
1. give the other party prompt written notice of the claim;
2. not make any admission and must not purport to settle the claim without the other party’s
prior written consent;
3. permit the other party to control the defence and settlement of the claim; and
4. reasonably cooperate with the other party (at the other party’s expense) in the defence
and settlement of the claim.
15) #### Warranties. [#warranties]
1. **Exclusion of warranties.** To the maximum extent permitted by Law, the Services are
provided on an “as-is” and “as-available” basis with no warranties other than those expressly
set out in this Agreement. Except to the extent prohibited by Law (or to the extent any
statutory rights apply that cannot be excluded, limited or waived), we and our related
companies and licensors:
1. make no representations or warranties of any kind, whether express, implied, statutory or
otherwise regarding the Services or Third-Party Components; and
2. disclaim all warranties, including any implied or express warranties: (i) of
merchantability, satisfactory quality, fitness for a particular purpose,
non-infringement, or quiet enjoyment; (ii) arising out of any course of dealing or usage
of trade; (iii) that the Services, Material or Third-Party Components will be
uninterrupted, error free or free of harmful components; and (iv) that any Content will
be secure or not otherwise lost or altered. However, see clause 19.15.
16) #### Liability. [#liability]
1. **Limitation.** Subject to clauses 16.2 and 16.3, and except for payment obligations under
clause 14.2, in no case will our and our related companies’ and licensors’ aggregate
liability under this Agreement exceed the amount you actually pay us under this Agreement for
the Service or Materials that gave rise to the claim during the 12 months before the
liability arose.
2. **Exclusion.** We and our related companies will not be liable to you or any other person for
any compensation, reimbursement, or damages:
1. for loss of profits, revenues, customers, opportunities, goodwill, use, or data or for
any indirect, incidental, special, consequential or exemplary damages, even if we have
been advised of the possibility of such damages;
2. arising out of or in connection with your inability to use the Services or Materials as a
result of: (i) this Agreement or your use of or access to the Services or Materials being
terminated or suspended for any reason, (ii) us discontinuing of some or all of the
Services or Materials in whole or in part, or (iii) downtime or unavailability of any
part of the Services or Materials, except as expressly stated in our
[Service Level Agreement](/docs/resources/terms/service-level-agreement); or
3. arising out of or in connection with your inability to use the Services or Materials in
connection with: (i) the cost of you procuring substitute goods or services; (ii)
investments, expenditures, or commitments by you in connection with this Agreement or
your use of or access to the Services or Materials; (iii) any unauthorised access to,
alteration of, or the deletion, destruction, damage, loss or failure to store any of Your
Content or other data that arises out of or in connection with your acts, omissions or
breach of this Agreement. However, see clause 19.15.
3. **Recovery of data.** Our liability for loss of or damage to Your Content is limited to
taking reasonable steps to recover (if practicable) the affected data from our available
backups.
4. **Application of Law.** The exclusions and limitations set out in this clause 16 apply only
to the maximum extent permitted by applicable Law. See clause 19.15.
17) #### Changing this Agreement. [#changing-this-agreement]
1. **Changes.** Subject to clause 17.2, we may change this Agreement from time to time by
publishing an updated version of the relevant document on the Site or notifying an update to
you in accordance with clause 19.10. Any such changes will come into effect 31 days after the
updated version is published or notified (or the earlier of the two if the change is both
published and notified). If the changes have a material detrimental impact, you may terminate
this Agreement before the changes come into effect under clause 12.1.
2. **Service Level Agreement changes.** For any changes to a Service Level Agreement, we will
provide you with at least three months’ advance notice in accordance with clause 19.10. If
the changes have a material detrimental impact, you may terminate this Agreement under clause
12.1 before the changes come into effect.
3. **Application of changes.** By continuing to use the Services or Materials after the
effective date of any changes to this Agreement, you agree to be bound by the modified terms.
Please check the Site regularly for changes to this Agreement. The date on which we last
changed each part of this Agreement is the date listed at the top of the relevant document
forming part of the Agreement.
18) #### Disputes. [#disputes]
1. **Addressing your concerns.** Most concerns can be resolved quickly and to everyone’s
satisfaction by contacting our support team. If we are unable to resolve your complaint to
your satisfaction (or if we haven’t been able to resolve a dispute we have with you after
attempting to do so informally), you and we agree to resolve those disputes through binding
arbitration in accordance with clause 18.2.
2. **Arbitration.** Any dispute or claim arising out of or in connection with this Agreement or
the Services will be resolved by binding arbitration using a sole arbitrator under the
Arbitration Act 1996 (NZ). The arbitration will be conducted in Auckland, New Zealand using
the English language in accordance with clause 19.5. If the parties cannot agree on an
arbitrator within five Working Days of a party issuing an arbitration notice, the arbitrator
will be appointed at the request of either party by the president for the time being of the
New Zealand Law Society or his or her nominee. Clauses 3 (Powers relating to conduct of
arbitral proceedings) and 6 (Costs and expenses of an arbitration) of the Second Schedule of
the Arbitration Act 1996 will apply (but no other clauses in the Second Schedule will apply).
3. **Urgent relief.** Nothing in this Agreement will prevent either party seeking or obtaining
any order or relief by way of injunction or declaration or other equitable or statutory
remedy against the other party where that party believes such order or relief is necessary
for the urgent protection of its rights or property, including Intellectual Property.
4. **Conduct of claims and litigation.** We and you agree that any dispute resolution
proceedings will be conducted only on an individual basis and not in a class, consolidated or
representative action. If for any reason a claim proceeds in court rather than in
arbitration, we and you waive any right to a jury trial.
19) #### General. [#general]
1. **Assignment.** You must not assign or otherwise transfer this Agreement or any of your
rights and obligations under this Agreement, except with our prior written consent. Any
assignment or transfer in violation of this clause 19 will be void. We may novate, assign or
otherwise transfer this Agreement without your consent:
1. in connection with a merger, acquisition or sale of all or substantially all of our
assets; or
2. to any related company or as part of a corporate reorganisation, and in the event of a
novation or transfer, effective upon the novation or transfer, the new party nominated by
us is deemed substituted for us as a party to this Agreement and we are fully released
from all of our obligations and duties to perform under this Agreement. Subject to the
foregoing, this Agreement will be binding upon, and inure to the benefit of the parties
and their respective permitted successors and assigns.
2. **Entire agreement.** This Agreement is the entire agreement between you and us regarding its
subject matter. It supersedes all prior or contemporaneous representations, understandings,
agreements, or communications between you and us, (whether written or verbal) regarding the
subject matter of this Agreement. We will not be bound by any term, condition or other
provision that is different from or in addition to the provisions of this Agreement (whether
or not it would materially alter this Agreement, and whether or not they are prior to,
contemporaneous with, or subsequent to this Agreement) including terms contained in your
purchase orders, receipts, confirmations, RFx documentation and other standard terms.
3. **Priority.** Where there is any conflict or ambiguity between this document and any other
document, Service Terms or Service Level Agreement forming part of this Agreement, the
following descending order of priority will apply:
1. applicable Service Terms;
2. this Customer Agreement document;
3. applicable Service Level Agreements.
4. **Force majeure.** We and our related companies will not be liable for any delay or failure
to perform any obligation under this Agreement where the delay or failure results from any
cause beyond our reasonable control, including acts of God, labour disputes or other
industrial disturbances, electrical or power outages, utilities or other telecommunications
or cloud infrastructure failures, earthquake, storms or other elements of nature, epidemics,
pandemics, blockages, embargoes, riots, acts or orders of government, acts of terrorism, or
war.
5. **Governing Law and jurisdiction.** This Agreement will be governed by the Laws of New
Zealand and you consent to the non-exclusive jurisdiction of the New Zealand courts. You must
not object to the transfer of any proceedings to New Zealand courts on any basis, including
inconvenience. The parties agree that United Nations Convention on Contracts for the
International Sale of Goods does not apply to this Agreement.
6. **Export control.** Each party will comply with all applicable export control laws and
regulations. You are solely responsible for your compliance with applicable laws in relation
to how you choose to use the Services, including the transfer and processing of Your Content,
the provision of Your Content to End Users, and the Region in which any of the foregoing
occur. You must not use our Services in violation of any export or trade embargo laws that
apply to you. You represent and warrant that you are not in a jurisdiction subject to
sanctions that would affect our provision of the Services or Material or otherwise designated
on any list of prohibited or restricted parties maintained by the United Nations Security
Council, the European Union or its Member States, the United States, New Zealand or other
applicable government authority.
7. **Our relationship with you.** We and you are independent contractors, and this Agreement
will not be construed to create a partnership, joint venture, agency, or employment
relationship. Neither party, nor any of their respective related companies, or personnel is
an agent of the other for any purpose or has the authority to bind the other.
8. **Language.** All communications and notices made or given pursuant to this Agreement must be
in the English language. If we provide a translation of the English language version of this
Agreement, the English language version of the Agreement will control if there is any
conflict.
9. **Confidentiality and publicity.** You may use MATTR Confidential Information only in
connection with your use of the Services and Materials as permitted under this Agreement. You
must not disclose MATTR Confidential Information without our written agreement. You must take
all reasonable measures to avoid disclosure, dissemination or unauthorised use of MATTR
Confidential Information, including, at a minimum, those measures you take to protect your
own confidential information of a similar nature. You must not issue any press release or
make any other public communication with respect to us, this Agreement or the Services or
Materials except with our prior written approval.
10. **Giving you notices.** We may provide notice to you under this Agreement by sending a
message to the email address associated with your account, or by posting a visible notice on
the Site or through the Services. Notices we provide by posting on the Site will be
effective upon posting, and notices we provide by email will be effective when we send the
email. It is your responsibility to keep your email address current. You will be deemed to
have received any email sent to the email address then associated with your account when we
send the email, whether or not you actually receive the email and whether or not we receive
a “bounce-back” or other notice of non-delivery.
11. **Giving us notices.** Any notice you send to us must be sent to [accounts@mattr.global](mailto:accounts@mattr.global) and
will be effective upon receipt into that inbox, provided that if received after 5 PM (NZST
or NZDT, as applicable) on a Working Day, they will be deemed received at 9 AM (NZST or
NZDT, as applicable) on the next Working Day.
12. **Third-Party beneficiaries.** The terms set out in clause 14 relating to indemnified third
parties and the references in this Agreement to our related companies and licensors are
expressed for the benefit of that person for the purposes of the Contract and Commercial Law
Act 2017 (Part 2, Subpart 1). Otherwise, this Agreement does not create any third-party
beneficiary rights in any individual or entity that is not a party to this Agreement.
13. **No waiver.** The failure by us to enforce any provision of this Agreement will not
constitute a present or future waiver of such provision nor limit our right to enforce such
provision or any other provision at a later time. All waivers by us must be in writing to be
effective.
14. **Severability.** If any portion of this Agreement is held to be invalid or unenforceable,
the remaining portions of this Agreement will remain in full force and effect. Any invalid
or unenforceable portions will be interpreted to give effect to the intent of the original
portion. If such construction is not possible, the invalid or unenforceable portion will be
severed from this Agreement, but the rest of the Agreement will remain in full force and
effect.
15. **Consumer Laws.** In some places, like New Zealand and Australia, there may be
non-excludable warranties, guarantees or other rights provided by Law (**Non-excludable
Consumer Guarantees**). They still apply – these terms do not exclude, restrict or modify
them. Except for such Non-excludable Consumer Guarantees and other rights you have that we
cannot exclude, we expressly exclude all warranties and guarantees and we are only bound by
the express terms set out in this Agreement. Our liability for breach of a Non-excludable
Consumer Guarantee is limited, at our option (and subject to clause 16.1), to either
re-performing, refunding, replacing or paying the cost of replacing the relevant service
(unless the Non-excludable Consumer Guarantee says otherwise).
20) #### Definitions and interpretation. [#definitions-and-interpretation]
1. **Definitions.** In this Agreement, the following terms will have the meaning set out below:
**Account** means the account, and associated log-in details that you’ll need to use in
order to access the Services.
**Account Information** means:
1. information about you that you provide to us in connection with the creation or
administration of your Account. For example, names, usernames, phone numbers, email
addresses and billing information associated with your Account; and
2. usage data related to your Account, such as resource identifiers, metadata tags, security
and access roles, rules, usage policies, permissions, usage statistics and analytics.
**Agreement** means this Customer Agreement document, together with the applicable Service
Terms and Service Level Agreements.
**API** means an application programming interface.
**Content** means software (including machine images), data, metadata, documents, text,
audio, video, images and other materials.
**Data Processing Terms** means the document with that title which we provide to you or make
available on the Site or through the Service, as may be updated by us from time to time.
**Documentation** means the user guides and admin guides (in each case exclusive of content
referenced via hyperlink) for the Services located at [https://learn.mattr.global](https://learn.mattr.global) (or any
updated URL designated by us), as such user guides and admin guides may be updated by us
from time to time.
**Effective Date** means the date when you accept the Customer Agreement, or otherwise
access or use any of the Services or Materials, whichever is earlier.
**End User** means your customers and any other individual or entity that directly or
indirectly:
1. accesses or uses Your Content; or
2. otherwise accesses, uses or benefits from the Services or Materials under your Account or
through services you provide to them.
**Feedback** means all ideas, suggestions and other feedback that you provide to us.
**Fees** means all amounts owing under this Agreement for the Services and Materials, as
described on the Site or otherwise notified by us to you at the time you order those
Services and Materials.
**Goods and Services Taxes** includes New Zealand goods and services Tax chargeable in
accordance with the Goods and Services Tax Act 1985 and any goods and services Tax, value
added Tax or sales Tax imposed under the laws of any other jurisdiction.
**Intellectual Property** means all intellectual property rights, including:
1. patents, designs, trademarks, service marks, copyright material or works, registered
designs, database rights, domain name rights, trade names, symbols and logos (whether
registered or unregistered); and
2. all formulae, methods, plans, data, drawings, specifications, characteristics,
algorithms, source and object code, equipment, designs, inventions, discoveries,
improvements, know-how, software, trade secrets and other proprietary information.
**Law** or **Laws** means:
1. any statute, regulation, bylaw, ordinance or subordinate legislation;
2. any binding court order, judgment or decree;
3. any binding order of any other governmental body; and
4. any applicable industry code, convention, policy or standard enforceable by law.
**Losses** means any claims, damages, losses, liabilities, costs, and expenses (including
reasonable lawyers’ fees).
**Material** means the MATTR Content and the MATTR Marks. Material does not include any
Third-Party Components.
**MATTR Confidential Information** means all non-public information disclosed by us, our
related companies, business partners or our or their respective employees, contractors or
agents that is designated as confidential or that, given the nature of the information or
circumstances surrounding its disclosure, reasonably should be understood to be
confidential. MATTR Confidential Information includes:
1. non-public information relating to our or our related companies or business partners’
goods, services, software, technology, processes, systems, customers, business plans,
promotional and marketing activities, finances and other business affairs;
2. third-party information that we are obliged to keep confidential; and
3. the nature, content and existence of any discussions or negotiations between you and us
or our related companies.
MATTR Confidential Information does not include any information that:
1. is or becomes publicly available without breach of this Agreement;
2. can be shown by documentation to have been known to you at the time of your receipt from
us;
3. is received from a third party who did not acquire or disclose the same by a wrongful or
tortious act; or
4. can be shown by documentation to have been independently developed by you without
reference to the MATTR Confidential Information.
**MATTR Content** means Content we or any of our related companies make available in
connection with the Services or on the Site to allow access to and use of the Services,
including APIs, WSDLs, SDKs, Documentation, sample code, software libraries, command line
tools, proofs of concept, templates and other related technology (including any of the
foregoing that are provided by our personnel). MATTR Content does not include the Services
or Third-Party Components.
**MATTR Marks** means any trademarks, service marks, service or trade names, domain names,
logos, and other designations of us and our related companies that we may make available to
you in connection with this Agreement.
**Month** or **Monthly** means the period from a day of one month to the corresponding day
of the next month, or if such does not exist, the last day of the next month.
**Overdue Interest Rate** means the “Business Overdraft Base Rate” of ANZ Bank New Zealand
Limited (**Base Rate**) plus 1.5% per annum (or, if the Base Rate ceases to be published,
such alternative rate as notified by us from time to time).
**Privacy Policy** means the privacy policies located on the Site (and any successor or
related locations designated by us), as may be updated by us from time to time.
**Region** means the geographical location where we will store and process Your Content as
part of the Services. We may also enable multiple geographic locations and allow you to
select from such locations for the storage and processing of Your Content.
**Service** means our platform, software offerings, or capabilities and any other services
made available by us or our related companies under this Agreement (however accessed and
including their presentation on the Site).
**Service Terms** means, in respect of a Service, the terms and conditions that apply to
your use of that Service as notified by us to you or published on our Site.
**Service Credit** means the dollar credit that we may credit back to an eligible Account in
accordance with a Service Level Agreement.
**Service Level Agreement** means a service level agreement for the relevant Services, which
we provide to you or otherwise set out on the Site (or any updated URL designated by us) as
may be updated by us from time to time.
**Site** means our website at mattr.global and learn.mattr.global as may be updated by us
from time to time, or any derivative, related or new site on which we offer our Services or
you can access our Materials.
**Site Terms** means the terms of use that relate to any use of the Site, located on the
Site (or any updated URL designated by us), as may be updated by us from time to time.
**Tax** or **Taxes** includes any present or future tax, levy, impost, duty, rate, charge or
fee imposed or levied by any government authority, whether in New Zealand or elsewhere,
together with any related interest, penalties or charges, but does not include a tax imposed
on or calculated by reference to overall net income.
**Term** means the term of this Agreement, as set out in clause 11.
**Termination Date** means the effective date of termination provided in accordance with
clause 12.
**Third-Party Components** has the meaning set out in clause 2.2.
**Working Days** means any day other than Saturday, Sunday or a public holiday observed in
Auckland, New Zealand.
**Your Content** means Content that you or your End Users transfer to us for processing,
storage or use in connection with the Services under your Account and any computational
results that you or any End User derive from the foregoing through their use of the
Services. For clarity, Your Content includes Content that you or any End User stores with us
in connection with our Services and Materials. Your Content does not include Account
Information.
**Your Systems** has the meaning given in clause 2.1.
2. **Interpretation.** In this Agreement:
1. a reference to any document, enactment or regulation includes a reference to that
document as amended or replaced from time to time;
2. headings appear as a matter of convenience and do not affect the meaning or construction
of the Agreement;
3. the word “includes” or “including” and similar terms do not limit the meaning of
preceding words;
4. a reference to any monetary amount is a reference to United States dollars;
5. a reference to a person includes a corporation sole and also a body of persons, whether
corporate or unincorporated;
6. the singular includes the plural and vice versa;
7. words importing one gender include the other genders; and
8. any rule of law or legal decision that would require interpretation of this Agreement
against the party that drafted it is not applicable and is hereby waived.
## Previous versions [#previous-versions]
[MATTR Customer Agreement - 25 March 2021 (archived)](/docs/resources/terms/customer-agreement/25-3-21)
# MATTR Customer Agreement (archived)
URL: /docs/resources/terms/customer-agreement/25-3-21
This document is provided for archival purposes only.
Last Updated: 25 March 2021[](#previous-versions)
This Agreement sets out the terms and conditions governing your access to and use of the Services
and Materials (as defined below). It is an agreement between MATTR Limited (**we**, **us**, or
**our**) and you or the entity you represent (**you** or **your**).
This Agreement takes effect when you click an “I Accept” button or tick box presented alongside
these terms, or when you otherwise access or use any of the Services or Materials (**Effective
Date**).
By entering into this Agreement, you warrant and represent that:
1. you are lawfully able to enter into contracts (e.g. you are not a minor); and
2. if you are entering into this Agreement for an entity (e.g. a company, government agency or
other organisation), you are authorised to do so and have legal authority to bind that entity.
1) #### Use of the Services and Materials. [#use-of-the-services-and-materials]
1. **General.** You may access and use the Services and Materials only in accordance with this
Agreement. As part of this Agreement, Service Level Agreements (**SLAs**) and Service Terms
apply to certain Services. Service Terms may specify additional Fees, restrictions and
obligations in connection with such Services. You must comply with the terms of this
Agreement and all Laws applicable to your use of the Services and Materials.
2. **Your account.** To access the Services, you must have an Account associated with a valid
email address and a valid form of payment.
3. **Third-Party Components.** You may use Third-Party Components that are made available to you
on the Site or through the Services. Third-Party Components are governed by this Agreement
and any separate terms and conditions accompanying such Third-Party Components (which may
include separate fees and charges).
4. **Trial period.** We may supply Services on a trial basis. Your Account may be suspended at
the end of the trial period unless you upgrade your Account to continue using or accessing
the Services.
2) #### Your Systems and Third-Party Components [#your-systems-and-third-party-components]
1. **Your Systems.** We may need you to provide us with access to any interfaces, software,
platforms, systems or infrastructure that are not owned or operated by us in order to supply
a Service to you (**Your Systems**). For each of Your Systems:
1. it is a condition of our supply of the Service that you obtain the necessary access for
us on an ongoing basis (including permissions and credentials) for as long as required by
us;
2. we will undertake that access on your behalf. You appoint us as your agent for this
purpose and consent to us representing to third parties that we are your agent for this
purpose;
3. if any charges or fees apply for that access, we may require you to pay those charges and
fees before we supply the Service, or we may pay those charges and fees and recover them
from you as Fees (with a reasonable administrative charge if specified in the relevant
Service Terms); and
4. you will defend, hold harmless and indemnify us from and against (i) any claim or action
brought by a third party, including all damages, liabilities, costs and expenses, and
lawyers’ fees on a solicitor and own-client basis arising out of or in connection with
our use of or access to Your System in accordance with this Agreement.
2. **Third-Party Components**
1. You acknowledge that some Services that we provide are developed or provided by third
parties and not by us (**Third-Party Components**).
2. Certain Third-Party Components we provide are subject to additional Service Terms, and
may subject to a third-party licence. As set out in the Service Terms, these are provided
on an ‘as-is’ basis without any warranty of any kind. If there is any conflict between
this Agreement and that separate licence, the separate licence will prevail with respect
of the Third-Party Components.
3. We, not the Third-Party Components provider, are responsible for the supply. The
Third-Party Components provider and its licensors provide no warranties, support or
indemnities in respect of the Third-Party Components.
3) #### Changes [#changes]
1. **Services.** We may change or discontinue any of the Services from time to time, subject to
any SLA we have agreed with you.
2. **Critical changes.** We may make changes referred to in clause 3.1 without notice and
despite any SLA to:
1. address or avoid a security or Intellectual Property risk to us or the Services or
Materials;
2. to avoid any violation of any Law; or
3. maintain the commercial viability, security and availability of the Services or
Materials.
3. **Service Level Agreements.** We may change, discontinue or add Service Level Agreements from
time to time in accordance with clause 17.
4) #### Data security and storage [#data-security-and-storage]
1. **Data security.** Without limiting clause 15 or your obligations under clause 6, we will
implement reasonable measures designed to help you secure Your Content against unauthorised
access, interference, modification, loss, or disclosure.
2. **Data region.** Where we allow you to select from a number of available Regions, you may
choose the Region in which Your Content will be processed and stored as part of the Services.
You consent to the storage of Your Content in, and transfer of Your Content into, the Region
you have selected. To provide billing, administration and support services, we may use and
process your Account Information in the Region you have selected and in another Region where
our billing, administration and support services are located.
3. **Data access.** We will not access, use or disclose Your Content except as is necessary or
required to:
1. maintain or provide the Services or Materials;
2. comply with the Law or a binding order of a governmental body; or
3. mitigate potential negative implications of a disaster or security incident (as
determined by us) which may include transfer of your data to a different Region.
4. **Legally requested disclosure.** We will not disclose Your Content or Account Information
except as necessary to comply with the Law or legal processes, or to exercise, establish or
defend our legal rights. If we are allowed to, we will notify you of any request or proposal
to disclose your information.
5) #### Data use and privacy [#data-use-and-privacy]
1. **Use of Data.** We respect you and your End Users’ privacy and take data protection
seriously. We will only use your Account Information in accordance with our Privacy Policy,
which describes in more detail how we deal with Account Information and the personal data of
End Users. You must ensure that you comply with all applicable data protection and privacy
laws, including by obtaining the consent of each End User for the collection, use, storage,
transfer, processing and disclosure of personal information, sensitive information or special
category data (as those terms are defined in applicable data protection and privacy laws).
2. **Data Processing Terms.** Depending on the location of your End Users and other individuals
whose personal information will be collected, used, stored, transferred, processed or
disclosed as a result of you using or accessing the Services or Materials, our
[Data Processing Terms](/docs/resources/terms/data-processing-terms) may apply.
3. **Anonymised statistical data.** When you use our Services, we may create anonymised
statistical data from your data and usage of our Services, including through aggregation.
Once anonymised, we may use it for our own purposes, such as to provide and improve our
Services, to identify any unacceptable use of our Services, to develop new services or
product offerings, to identify business trends, and for other uses we communicate to you.
4. **Data breach notifications.** If we think there has been unauthorised access to, or
disclosure of, personal information inside your Account, we will let you know and endeavour
to give you information about what happened. Depending on the nature of the unauthorised
access or disclosure, and the location of those affected, you may be required to assess
whether the unauthorised access or disclosure must be reported to those affected and/or a
relevant authority. We will rely on you to make this decision, because you will have the most
knowledge about the personal information stored in your Account.
6) #### Your responsibilities. [#your-responsibilities]
1. **Your accounts.** Except to the extent caused by our breach of this Agreement:
1. you are responsible for all activities that occur under your Account, regardless of
whether the activities are authorised by you or undertaken by you, your employees or a
third party (including your contractors, agents or End Users); and
2. we and our related companies are not responsible for unauthorised access, interference or
modification to your Account, or any unauthorised access, modification, loss or
disclosure of Your Content or personal or other data in your Account.
2. **Your Content and use of the Services.** You must ensure that Your Content, your, and your
End Users’ use of Your Content, Services and Materials comply with this Agreement, all
applicable Laws and all restrictions described in the MATTR Content and on the Site, and any
other policy or terms referenced in or incorporated into this Agreement. You are solely
responsible for the development, content, operation, maintenance, and use of Your Content.
3. **Securing and protecting Your Content.** Except to the extent we have agreed in writing or
expressly offered as part of the Services, you are responsible for:
1. properly configuring and using the Services and Materials; and
2. taking reasonable and appropriate action to secure, protect and backup your Account and
Your Content in a manner that will provide appropriate security and protection (which
might include use of encryption to protect Your Content) from unauthorised access,
interference, modification, loss or disclosure and routinely archiving Your Content.
4. **Account log-In details and private keys.** Account log-in details and private keys
generated by the Services are for your internal use only. You are responsible for protecting
them against unauthorised use or disclosure and you must not sell, transfer or sublicense
them, except:
1. to the extent that we otherwise permit in writing (which we may permit subject to
conditions); and
2. that you may disclose your private key to your agents and contractors performing work on
your behalf.
5. **End Users.** You are responsible for your End Users’ use of Your Content, the Services and
the Materials. You must ensure all End Users comply with the terms of this Agreement and
agree to terms that are consistent with this Agreement. We do not have to provide any support
or services to End Users unless we have a separate agreement with you or them for the
provision of such support or services or are required to support them by Law (e.g. with
access to personal information).
7) #### Unacceptable use [#unacceptable-use]
1. **Overview.** You must not, and must ensure that End Users will not, use the Services or Site
in any manner or for any purpose other than as expressly permitted by this Agreement. The
examples described in this clause are not exhaustive. If you or your End Users do not comply
with this clause, we may suspend or terminate your use of the Services in accordance with
clauses 10 or 12 as appropriate.
2. **Illegal, harmful, or offensive use or content.** You must not use, or encourage, promote,
facilitate or instruct others to use, the Services or Site in a manner that is illegal,
harmful, fraudulent, offensive or that infringes on the right of any person, or to transmit,
store, display, distribute or otherwise make available content that we consider to be
illegal, harmful, fraudulent, or offensive.
3. **Malware.** You must not use the Services or Site to host or distribute any Content or other
computer technology that may damage, interfere with, surreptitiously intercept, or
expropriate any system, program, or data (including viruses, Trojan horses, worms, time
bombs, cancelbots and other malware).
4. **Unauthorised access.** You must not access or use the Services to threaten, attempt to, or
engage in conduct that would violate the security or integrity of (including though a
malicious act), or otherwise gain unauthorised access to, any communication, network,
computer or communications system, software application, or network or computing device.
5. **Network interference.** You must not threaten, attempt, or engage in, conduct that is
likely to interfere with, pose a security risk to, or adversely impact our systems, Services,
or Site, including the Content of our customers and their use of our Services or Site. For
clarity, this includes making network connections to any computer system or network (unless
you have permission to do so), monitoring or crawling systems or networks in a way that
impairs or disrupts them, denial of service attacks, intentionally interfering with any
computer system or network, or using any means to avoid usage limitations and restrictions
placed on the Services or any other computer system or network.
6. **License restrictions.** You must not breach the license restrictions set out in clause
13.4.
7. **Monitoring and enforcement.** We may, but are not obliged to, investigate any violation of
this clause or any other misuse of the Services or Site. We may report any activity that we
suspect violates any Law to law enforcement officials, regulators, or other appropriate third
parties. Our reporting may include disclosing Account Information. We also may cooperate with
law enforcement agencies, regulators, or other appropriate third parties to help with the
investigation and prosecution of illegal conduct by providing network and systems information
related to alleged violations of this clause.
8) #### Fees and payment [#fees-and-payment]
1. **Fees.** We calculate and charge or invoice Fees Monthly or as otherwise agreed. You must
pay us the Fees for use of the Services and Materials by the due date for payment and using
one of the payment methods we have approved for your Account. You must pay all Fees payable
under this Agreement without any set-off, counterclaim, deduction or other withholding. We
may charge you interest at the Overdue Interest Rate on unpaid Fees from the date such unpaid
Fees became due until the date payment is received into our account in cleared funds.
However, if the Overdue Interest Rate exceeds the maximum permitted legal interest rate, the
interest chargeable will be reduced to reflect the maximum permitted legal interest rate.
2. **Changes to Fees.** Fees and charges for new Services or service features will be effective
when we post updated Fees on the MATTR Site or notify you of the Fees, or such other date as
we expressly state. We may increase or add new Fees for existing Services at any time by
giving you at least 30 days’ prior notice.
9) #### Taxes [#taxes]
1. **Fees exclusive of Taxes.** Our Fees exclude Goods and Services Taxes and all other Taxes.
If we are liable for any Goods and Services Taxes or any other Tax in respect of the
provision of the Services or Materials or any other supply we make under this Agreement, you
will pay us, in addition to and at the same as our Fees, an amount equal to the amount of
such Tax, subject to receipt of a tax invoice (if applicable).
2. **Withholding Tax.** If:
1. you are required by Law to make any deduction or withholding from any amount payable to
us under this Agreement; or
2. we are required by Law to pay any Tax in relation to any amount receivable by us under
this Agreement, you must pay to us such additional amounts as are necessary so that,
after making the deduction, withholding or payment, the net amount received and retained
by us is equal to the amount we would have received and retained had no such deduction,
withholding or payment been made.
3. **Information.** If you deduct or withhold any amount from a payment to us under clause 9.2,
you will, at our request, provide us with reasonable evidence of payment of the deduction or
withholding to the relevant tax authority.
10) #### Suspension [#suspension]
1. **General.** We may suspend your right to access or use any or all Services immediately upon
notice to you to the extent that we determine:
1. your or your End User’s use of the Services or Materials: (i) is in breach of clauses 5,
6, or 7; (ii) could subject us, our related companies, or any third party to liability;
(iii) could be fraudulent; or (iv) is prohibited under clause 19.6;
2. you are in breach of this Agreement;
3. you are in breach of your payment obligations under clause 8 in respect of any Services
or Materials; or
2. you have ceased to operate in the ordinary course of business, made an assignment for the
benefit of creditors or similar disposition of your assets, or become the subject of any
bankruptcy, reorganisation, liquidation, dissolution or similar proceeding.
3. **Effect of suspension.** If we suspend your right to access or use any or all Services:
1. you remain responsible for all Fees you incur during the period of suspension; and
2. you will not be entitled to any Service Credits under the Service Level Agreements for
any period of suspension.
11) #### Term [#term]
1. **Term.** This Agreement will start on the Effective Date and will remain in effect unless
terminated under clause 12.
12) #### Termination [#termination]
1. **Termination without cause.** Unless we have agreed a committed contract term with you, you
can terminate this Agreement in respect of any or all Services and Materials for any reason
by closing your account for the relevant Services. To terminate in respect of all Services
and Materials, you must close your account for all Services for which we provide an Account
closing mechanism. We may also terminate this Agreement for any or all Services and Materials
for any reason by providing you with at least 30 days’ written notice for any reason except
to the extent we have agreed to a longer committed contract term with you.
2. **Termination for cause.** Either party may terminate this Agreement in respect of any
affected Services if the other party is in material breach of this Agreement and the breach
remains uncured for a period of 30 days from receipt of written notice by the other party.
You must close your Account for all affected Services for which we provide an account closing
mechanism no later than the date notified to you.
3. **Immediate termination by us.** We may also terminate this Agreement in respect of any or
all Services and Materials immediately upon notice to you:
1. to the extent we have the right to suspend under clause 10;
2. if our relationship with a third-party partner who provides software or other technology
we use to provide affected Services or Materials expires, terminates or requires us to
change the way we provide the software or other technology as part of the Services or
Materials;
3. to comply with any applicable Law or a binding order of a governmental body; or
4. if we are subject to any legal or regulatory changes that we consider make it technically
or commercially burdensome for us to continue providing the affected Services or
Materials to you.
4. **Effect of termination.** Upon the Termination Date:
1. except as provided in clause 12.5, all your rights under this Agreement with respect to
affected Services immediately terminate;
2. you remain responsible for all Fees you have incurred for affected Services up until, and
including, the Termination Date and are responsible for any Fees you incur for those
affected Services during the post-termination period described in clause 13.5;
3. you will immediately return or, if instructed by us, destroy all Material provided in
connection with affected Services which are in your possession; and
4. clauses 6.1, 8, 12, 13 (except the licence granted to you in clause 13.3), 14, 15, 16,
19, and 20 will continue to apply in connection with the affected Services.
5. **Consequences of termination.** Unless we terminate your use of the Services or Materials
pursuant to clause 12.2:
1. we will not take action to remove Your Content from the Services as a result of the
termination for 30 days following the Termination Date; and
2. we may remove Your Content from the Services any time following that 30 day period unless
otherwise agreed with you in writing.
6. **Use after Termination Date.** For any use of the Services or Materials after the
Termination Date, your obligations under this Agreement will apply, and you will pay the
applicable Fees in accordance with clause 8.
13) #### Intellectual Property [#intellectual-property]
1. **Your Content.** You own Your Content. When you transfer Your Content into our Services,
you grant us a non-exclusive, transferable and sublicensable licence to use, copy,
communicate, transmit, store, analyse, adapt and back up all data you submit to us through
our Services. You agree that we may use, transfer and sublicense Your Content to provide the
Services and Materials to you and your End Users.
2. **Adequate rights.** You represent and warrant to us that:
1. you or your licensors own all right, title, and interest in and to Your Content and
Feedback, including to Intellectual Property;
2. you have all rights in Your Content and Feedback necessary to grant the rights
contemplated by this Agreement; and
3. none of Your Content or End Users’ use of Your Content or the Services, Materials or Site
will violate clause 5, 6, or 7.
3. **Your licence to use our Services.** We or our licensors own all rights, title, and
interest in and to the Services, Site, or Materials, including to Intellectual Property.
Subject to the terms of this Agreement, we grant you a limited, revocable, non-exclusive,
non-transferrable licence for the Term of this Agreement to access and use the Services or
Materials solely in accordance with this Agreement, including subject to clauses 6.2 and 7.
The Services and Materials are not sub-licensable except to the extent set out in applicable
Service Terms or as otherwise notified to you. Except as provided in this clause 13.3, you
obtain no rights under this Agreement from us, our related companies or our licensors to the
Services or Materials, including any related Intellectual Property.
4. **Licence restrictions.** You must not, and must not permit End Users to, access or use the
Services or Materials in any manner or for any purpose other than as expressly permitted by
this Agreement. Neither you nor any End User will, or will attempt to:
1. modify, distribute, alter, tamper with, repair, or otherwise create derivative works of
any Content included in the Services or Materials (except to the extent Content included
in the Services is provided to you under a separate licence that expressly permits the
creation of derivative works);
2. reverse engineer, disassemble, or decompile the Services or Materials or apply any other
process or procedure to derive the source code of any software included in the Services
or Materials (except to the extent applicable Law doesn’t allow this restriction);
3. access or use the Services or Materials in a way intended to avoid incurring fees, bypass
usage limits or bypass quotas; or
4. resell or sublicense the Services or Materials except with our prior written agreement.
[Contact us](mailto:info@mattr.global) to discuss your implementation and requirements.
5. **MATTR Marks.** You may only use the MATTR Marks with our prior written permission and in
accordance with any trademark usage guidelines that we have published on the Site or
otherwise notified to you.
6. **Feedback.** You are not obliged to provide Feedback to us or our related companies. If you
do so:
1. we and our related companies may use, modify or develop the Feedback for any purpose without restriction and without attribution or compensation to you or any other person; and
2. you irrevocably assign to us all right, title, and interest in and to the Feedback and agree to provide us any assistance we require to document, perfect, and maintain our rights in the Feedback.
14) #### Indemnity [#indemnity]
1. **General.** You will defend, indemnify, and hold harmless us, our related companies and
licensors, and each of our and their respective employees, officers, directors, and
representatives from and against any third-party claim or Losses arising out of or in
connection with:
1. your or any End Users’ access to or use of the Services or Materials (including any
activities under your Account and use by your employees and personnel); or
2. breach of this Agreement or violation of applicable Laws by you, End Users or Your
Content; or
3. a dispute between you and any End User.
You will reimburse us for reasonable legal fees, as well as our employees’ and contractors’
time and materials spent responding to any third party subpoena or other compulsory legal
order or process associated with third party claims described in this clause at our
then-current hourly rates.
2. **The Services.** Subject to the limitations in this clause 14, we will defend you and your
employees, officers, and directors against any third-party claim alleging that the Services
infringe or misappropriate that third party’s Intellectual Property, and will pay the amount
of any adverse final judgment or any settlement agreed by us.
3. **Your Content.** Subject to the limitations in this clause 14, you will defend us, our
related companies, and our and their respective employees, officers, and directors against
any third-party claim alleging that any of Your Content infringes or misappropriates that
third party’s intellectual property rights, and will pay the amount of any adverse final
judgment or settlement agreed by you.
4. **Exclusions.** We will have no obligations or liability to you under clause 14.2 arising
out of or in connection with: 1. modification of the Services or Materials by anyone other
than us or our related companies; 2. combination, operation or use of the Services or
Materials with any other goods, software, product, data or services not provided by us;
or 3. your or any End User’s use of the Services or Materials after we have notified you to
discontinue such use. The remedies provided in this clause 14 are the sole and exclusive
remedies for any third-party claims of infringement or misappropriation of Intellectual
Property by the Services or by Your Content.
5. **Remediation.** For any claim covered by clause 14.2 we will, at our option, either:
1. procure the rights to use that portion of the Services alleged to be infringing;
2. replace the alleged infringing portion of the Services with a non-infringing alternative;
3. modify the alleged infringing portion of the Services to make it non-infringing; or
4. terminate the allegedly infringing portion of the Services or this Agreement or both (as
applicable).
6. **Process.** The party seeking defence or indemnity under this clause 14 must:
1. give the other party prompt written notice of the claim;
2. not make any admission and must not purport to settle the claim without the other party’s
prior written consent;
3. permit the other party to control the defence and settlement of the claim; and
4. reasonably cooperate with the other party (at the other party’s expense) in the defence
and settlement of the claim.
15) #### Warranties [#warranties]
1. **Exclusion of warranties.** To the maximum extent permitted by law, the Services are
provided on an “as-is” and “as-available” basis with no warranties other than those
expressly set out in this Agreement. Except to the extent prohibited by Law (or to the
extent any statutory rights apply that cannot be excluded, limited or waived), we and our
related companies and licensors:
1. make no representations or warranties of any kind, whether express, implied, statutory or otherwise regarding the Services or Third-Party Components; and
2. disclaim all warranties, including any implied or express warranties: (i) of merchantability, satisfactory quality, fitness for a particular purpose, non-infringement, or quiet enjoyment; (ii) arising out of any course of dealing or usage of trade; (iii) that the Services, Materials or Third-Party Components will be uninterrupted, error free or free of harmful components; and (iv) that any Content will be secure or not otherwise lost or altered. However, see clause 19.15.
16) #### Liability [#liability]
1. **Limitation.** Subject to clauses 16.2 and 16.3, and except for payment obligations under
clause 14.2, in no case will our and our related companies’ and licensors’ aggregate
liability under this Agreement exceed the amount you actually pay us under this Agreement
for the Service that gave rise to the claim during the 12 months before the liability arose.
2. **Exclusion.** We and our related companies will not be liable to you or any other person
for any compensation, reimbursement, or damages:
1. for loss of profits, revenues, customers, opportunities, goodwill, use, or data or for
any indirect, incidental, special, consequential or exemplary damages, even if we have
been advised of the possibility of such damages;
2. arising out of or in connection with your inability to use the Services or Materials as a
result of: (i) this Agreement or your use of or access to the Services or Materials being
terminated or suspended for any reason, (ii) us discontinuing of some or all of the
Services or Materials in whole or in part, or (iii) downtime or unavailability of any
part of the Services or Materials, except as expressly stated in our
[Service Level Agreement](/docs/resources/terms/service-level-agreement); or
3. arising out of or in connection with your inability to use the Services or Materials
arising in connection with: (i) the cost of you procuring substitute goods or services;
(ii) investments, expenditures, or commitments by you in connection with this Agreement
or your use of or access to the Services or Materials; (iii) any unauthorised access to,
alteration of, or the deletion, destruction, damage, loss or failure to store any of Your
Content or other data that arises out of or in connection with your acts, omissions or
breach of this Agreement. However, see clause 19.15.
3. **Recovery of data.** Our liability for loss of or damage to Your Content is limited to
taking reasonable steps to recover (if practicable) the affected data from our available
backups.
4. **Application of Law.** The exclusions and limitations set out in this clause 16 apply only
to the maximum extent permitted by applicable Law. See clause 19.15.
17) #### Changing this Agreement [#changing-this-agreement]
1. **Changes.** Subject to clause 17.2, we may change this Agreement from time to time by
publishing an updated version of the relevant document on the Site or notifying an update to
you in accordance with clause 19.10. Any such changes will come into effect 31 days after
the updated version is published or notified (or the earlier of the two if the change is
both published and notified). If the changes have a material detrimental impact, you may
terminate this Agreement before the changes come into effect under clause 12.1.
2. **Service Level Agreement changes.** For any changes to a Service Level Agreement, we will
provide you with at least three months’ advance notice in accordance with clause 19.10. If
the changes have a material detrimental impact, you may terminate this Agreement before the
changes come into effect under clause 12.1.
3. **Application of changes.** By continuing to use the Services or Materials after the
effective date of any changes to this Agreement, you agree to be bound by the modified
terms. Please check the Site regularly for changes to this Agreement. The date on which we
last changed each part of this Agreement is the date listed at the top of the relevant
document forming part of the Agreement.
18) #### Disputes [#disputes]
1. **Addressing your concerns.** Most concerns can be resolved quickly and to everyone’s
satisfaction by contacting our support team. If we are unable to resolve your complaint to
your satisfaction (or if we haven’t been able to resolve a dispute we have with you after
attempting to do so informally), you and we agree to resolve those disputes through binding
arbitration in accordance with clause 18.2.
2. **Arbitration.** Any dispute or claim arising out of or in connection with this Agreement or
the Services will be resolved by binding arbitration using a sole arbitrator under the
Arbitration Act 1996 (NZ). The arbitration will be conducted in Auckland, New Zealand using
the English language in accordance with clause 19.5. If the parties cannot agree on an
arbitrator within five Working Days of a party issuing an arbitration notice, the arbitrator
will be appointed at the request of either party by the president for the time being of the
New Zealand Law Society or his or her nominee. Clauses 3 (Powers relating to conduct of
arbitral proceedings) and 6 (Costs and expenses of an arbitration) of the Second Schedule of
the Arbitration Act 1996 will apply (but no other clauses in the Second Schedule will
apply).
3. **Urgent relief.** Nothing in this Agreement will prevent either party seeking or obtaining
any order or relief by way of injunction or declaration or other equitable or statutory
remedy against the other party where that party believes such order or relief is necessary
for the urgent protection of its rights or property, including Intellectual Property.
4. **Conduct of claims and litigation.** We and you agree that any dispute resolution
proceedings will be conducted only on an individual basis and not in a class, consolidated
or representative action. If for any reason a claim proceeds in court rather than in
arbitration, we and you waive any right to a jury trial.
19) #### General [#general]
1. **Assignment.** You must not assign or otherwise transfer this Agreement or any of your
rights and obligations under this Agreement, except with our prior written consent. Any
assignment or transfer in violation of this clause 19 will be void. We may novate this
Agreement without your consent: 1. in connection with a merger, acquisition or sale of all
or substantially all of our assets; or 2. to any related company or as part of a corporate
reorganisation, and and in the event of a novation or transfer, effective upon the novation
or transfer, the new party nominated by us is deemed substituted for us as a party to this
Agreement and we are fully released from all of our obligations and duties to perform under
this Agreement. Subject to the foregoing, this Agreement will be binding upon, and inure to
the benefit of the parties and their respective permitted successors and assigns.
2. **Entire agreement.** This Agreement is the entire agreement between you and us regarding
its subject matter. It supersedes all prior or contemporaneous representations,
understandings, agreements, or communications between you and us, (whether written or
verbal) regarding the subject matter of this Agreement. We will not be bound by any term,
condition or other provision that is different from or in addition to the provisions of this
Agreement (whether or not it would materially alter this Agreement) including terms
contained in your purchase orders, receipts, confirmations, RFx documentation and other
standard terms.
3. **Priority.** Where there is any conflict or ambiguity between this document and any other
document, Service Terms or Service Level Agreement forming part of this Agreement, the
following descending order of priority will apply: 1. applicable Service Terms; 2. this
Customer Agreement document; 3. applicable Service Level Agreements.
4. **Force majeure.** We and our related companies will not be liable for any delay or failure
to perform any obligation under this Agreement where the delay or failure results from any
cause beyond our reasonable control, including acts of God, labour disputes or other
industrial disturbances, electrical or power outages, utilities or other telecommunications
or cloud infrastructure failures, earthquake, storms or other elements of nature, epidemics,
pandemics, blockages, embargoes, riots, acts or orders of government, acts of terrorism, or
war.
5. **Governing Law and jurisdiction.** This Agreement will be governed by the Laws of New
Zealand and you consent to the non-exclusive jurisdiction of the New Zealand courts. You
must not object to the transfer of any proceedings to New Zealand courts on any basis,
including inconvenience. The parties agree that United Nations Convention on Contracts for
the International Sale of Goods does not apply to this Agreement.
6. **Export control.** Each party will comply with all applicable export control laws and
regulations. You are solely responsible for your compliance with applicable laws in relation
to how you choose to use the Services , including the transfer and processing of Your
Content, the provision of Your Content to End Users, and the Region in which any of the
foregoing occur. You must not use our Services in violation of any export or trade embargo
laws that apply to you. You represent and warrant that you are not in a jurisdiction subject
to sanctions or otherwise designated on any list of prohibited or restricted parties
maintained by the United Nations Security Council, the European Union or its Member States,
the United States or other applicable government authority.
7. **Our relationship with you.** We and you are independent contractors, and this Agreement
will not be construed to create a partnership, joint venture, agency, or employment
relationship. Neither party, nor any of their respective related companies, is an agent of
the other for any purpose or has the authority to bind the other.
8. **Language.** All communications and notices made or given pursuant to this Agreement must
be in the English language. If we provide a translation of the English language version of
this Agreement, the English language version of the Agreement will control if there is any
conflict.
9. **Confidentiality and publicity.** You may use MATTR Confidential Information only in
connection with your use of the Services as permitted under this Agreement. You must not
disclose MATTR Confidential Information without our written agreement. You must take all
reasonable measures to avoid disclosure, dissemination or unauthorised use of MATTR
Confidential Information, including, at a minimum, those measures you take to protect your
own confidential information of a similar nature. You must not issue any press release or
make any other public communication with respect to us, this Agreement or the Services
except with our prior written approval.
10. **Giving you notices.** We may provide notice to you under this Agreement by sending a
message to the email address associated with your account, or by posting a visible notice on
the Site or through the Services. Notices we provide by posting on the Site will be
effective upon posting, and notices we provide by email will be effective when we send the
email. It is your responsibility to keep your email address current. You will be deemed to
have received any email sent to the email address then associated with your account when we
send the email, whether or not you actually receive the email and whether or not we receive
a “bounce-back” or other notice of non-delivery.
11. **Giving us notices.** Any notice you send to us must be sent to [accounts@mattr.global](mailto:accounts@mattr.global) and
will be effective upon receipt into that inbox, provided that if received after 5 PM (NZST
or NZDT, as applicable) on a Working Day, they will be deemed received at 9 AM (NZST or
NZDT, as applicable) on the next Working Day.
12. **Third-Party beneficiaries.** The terms set out in clause 14 relating to indemnified third
parties and the references in this Agreement to our related companies and licensors are
expressed for the benefit of that person for the purposes of the Contract and Commercial Law
Act 2017 (Part 2, Subpart 1). Otherwise, this Agreement does not create any third-party
beneficiary rights in any individual or entity that is not a party to this Agreement.
13. **No waiver.** The failure by us to enforce any provision of this Agreement will not
constitute a present or future waiver of such provision nor limit our right to enforce such
provision or any other provision at a later time. All waivers by us must be in writing to be
effective.
14. **Severability.** If any portion of this Agreement is held to be invalid or unenforceable,
the remaining portions of this Agreement will remain in full force and effect. Any invalid
or unenforceable portions will be interpreted to effect and intent of the original portion.
If such construction is not possible, the invalid or unenforceable portion will be severed
from this Agreement, but the rest of the Agreement will remain in full force and effect.
15. **Consumer Laws.** In some places, like New Zealand and Australia, there may be
non-excludable warranties, guarantees or other rights provided by Law (**Non-excludable
Consumer Guarantees**). They still apply – these terms do not exclude, restrict or modify
them. Except for such Non-excludable Consumer Guarantees and other rights you have that we
cannot exclude, we expressly exclude all warranties and guarantees and we are only bound by
the express terms set out in this Agreement. Our liability for breach of a Non-excludable
Consumer Guarantee is limited, at our option (and subject to clause 16.1), to either
re-performing, refunding, replacing or paying the cost of replacing the relevant service
(unless the non-excludable consumer guarantee says otherwise).
20) #### Definitions and interpretation [#definitions-and-interpretation]
1. **Definitions.** In this Agreement, the following terms will have the meaning set out below:
**Account** means the account, and associated log-in details that you’ll need to use in
order to access the Services.
**Account Information** means:
1. information about you that you provide to us in connection with the creation or administration of your Account. For example, names, usernames, phone numbers, email addresses and billing information associated with your Account, and
2. usage data related to your Account, such as resource identifiers, metadata tags, security and access roles, rules, usage policies, permissions, usage statistics and analytics.
**Agreement** means this Customer Agreement document, together with the applicable Service
Terms and Service Level Agreements.
**API** means an application program interface.
**Content** means software (including machine images), data, metadata, documents, text,
audio, video, images and other materials.
**Documentation** means the user guides and admin guides (in each case exclusive of content
referenced via hyperlink) for the Services located at [https://learn.mattr.global](https://learn.mattr.global) (or any
updated URL designated by us), as such user guides and admin guides may be updated by us
from time to time.
**Effective Date** means the date when you accept the Customer Agreement, or otherwise
access or use any of the Services or Materials, whichever is earlier.
**End User** means your customers and any other individual or entity that directly or
indirectly:
1. accesses or uses Your Content; or
2. otherwise accesses, uses or benefits from the Services under your Account or through
services you provide to them.
**Feedback** means all ideas, suggestions and other feedback that you provide to us.
**Fees** means all amounts owing under this Agreement for the Services and Materials, as
described on the Site or otherwise notified by us to you at the time you order those
Services and Materials.
**Goods and Services Taxes** includes New Zealand goods and services Tax chargeable in
accordance with the Goods and Services Tax Act 1985 and any goods and services Tax, value
added Tax or sales Tax imposed under the laws of any other jurisdiction.
**Intellectual Property** means all intellectual property rights, including:
1. patents, designs, trademarks, service marks, copyright material or works, registered
designs, trade names, symbols and logos (whether registered or unregistered); and
2. all formulae, methods, plans, data, drawings, specifications, characteristics,
algorithms, source and object code, equipment, designs, inventions, discoveries,
improvements, know-how, software, trade secrets and other proprietary information.
**Law** or **Laws** means:
1. any statute, regulation, bylaw, ordinance or subordinate legislation;
2. any binding court order, judgment or decree;
3. any binding order of any other governmental body; and
4. any applicable industry code, convention, policy or standard enforceable by law.
**Losses** means any claims, damages, losses, liabilities, costs, and expenses (including
reasonable lawyers’ fees).
**Material** means the MATTR Content and the MATTR Marks. Material does not include any
Third-Party Components.
**MATTR Confidential Information** means all non-public information disclosed by us, our
related companies, business partners or our or their respective employees, contractors or
agents that is designated as confidential or that, given the nature of the information or
circumstances surrounding its disclosure, reasonably should be understood to be
confidential. MATTR Confidential Information includes:
1. non-public information relating to our or our related companies or business partners’
technology, customers, business plans, promotional and marketing activities, finances and
other business affairs;
2. third-party information that we are obligated to keep confidential; and
3. the nature, content and existence of any discussions or negotiations between you and us
or our related companies.
MATTR Confidential Information does not include any information that:
1. is or becomes publicly available without breach of this Agreement;
2. can be shown by documentation to have been known to you at the time of your receipt from
us;
3. is received from a third party who did not acquire or disclose the same by a wrongful or
tortious act; or
4. can be shown by documentation to have been independently developed by you without
reference to the MATTR Confidential Information.
**MATTR Content** means Content we or any of our related companies make available in
connection with the Services or on the Site to allow access to and use of the Services,
including APIs, WSDLs, Documentation, sample code, software libraries, command line tools,
proofs of concept, templates and other related technology (including any of the foregoing
that are provided by our personnel). MATTR Content does not include the Services or
Third-Party Components.
**MATTR Marks** means any trademarks, service marks, service or trade names, logos, and
other designations of us and our related companies that we may make available to you in
connection with this Agreement.
**Month** or **Monthly** means the period from a day of one month to the corresponding day
of the next month, or if such does not exist, the last day of the next month.
**Overdue Interest Rate** means the “Business Overdraft Base Rate” of ANZ Bank New Zealand
Limited (Base Rate) plus 1.5% per annum (or, if the Base Rate ceases to be published, such
alternative rate as notified by us from time to time).
**Privacy Policy** means the privacy policies located on the Site (and any successor or
related locations designated by us), as it may be updated by us from time to time.
**Region** means the geographical location where we will store and process Your Content as
part of the Services. We may also enable multiple geographic locations and allow you to
select from such locations for the storage and processing of Your Content.
**Service** means our platform, software offerings, or capabilities and any other services
made available by us or our related companies under this Agreement (however accessed and
including their presentation on the Site).
**Service Terms** means, in respect of a Service, the terms and conditions that apply to
your use of that Service as notified by us to you or published on our Site.
**Service Credit** means the dollar credit that we may credit back to an eligible Account in
accordance with a Service Level Agreement.
**Service Level Agreement** means a service level agreement for the relevant Services, which
we provide to you or otherwise set out on the Site (or any updated URL designated by us) and
may be updated by us from time to time.
**Site** means our website at mattr.global and learn.mattr.global as may be updated by us
from time to time, or any derivative, related or new Site on which we offer our Services or
you can access our Materials.
**Site Terms** means the terms of use that relate to any use of the Site, located on the
Site (or any updated URL designated by us), as may be updated by us from time to time.
**Tax** or **Taxes** includes any present or future tax, levy, impost, duty, rate, charge or
fee imposed or levied by any government authority, whether in New Zealand or elsewhere,
together with any related interest, penalties or charges, but does not include a tax imposed
on or calculated by reference to overall net income.
**Term** means the term of this Agreement, as set out in clause 11.
**Termination Date** means the effective date of termination provided in accordance with
clause 12.
**Third-Party Components** has the meaning set out in clause 2.2.
**Third-Party Component** means third-party Content made available to you on the Site or in
conjunction with the Services.
**Working Days** means any day other than Saturday, Sunday or a public holiday observed in
Auckland, New Zealand.
**Your Content** means Content that you or your End Users transfer to us for processing,
storage or use in connection with the Services under your Account and any computational
results that you or any End User derive from the foregoing through their use of the
Services. For clarity, Your Content includes Content that you or any End User stores with us
in connection with our Services and Materials. Your Content does not include Account
Information.
**Your Systems** has the meaning given in clause 2.1.
2. **Interpretation.** In this Agreement:
1. a reference to any document, enactment or regulation includes a reference to that
document as amended or replaced from time to time;
2. headings appear as a matter of convenience and do not affect the meaning or construction
of the Agreement;
3. the word “includes” or “including” and similar terms do not limit the meaning of
preceding words;
4. a reference to any monetary amount is a reference to United States dollars,
5. a reference to a person includes a corporation sole and also a body of persons, whether
corporate or unincorporated;
6. the singular includes the plural and vice versa;
7. words importing one gender include the other genders; and
8. any rule of law or legal decision that would require interpretation of this Agreement
against the party that drafted it is not applicable and is hereby waived.
# MATTR Customer Agreement (archived)
URL: /docs/resources/terms/customer-agreement/30-6-22
This document is provided for archival purposes only.
Last Updated: 30 June 2022
[See what's changed](/docs/resources/terms/customer-agreement/recent-changes) |
[Previous versions](#previous-versions)
This Agreement sets out the terms and conditions governing your access to and use of the Services
and Materials (as defined below). It is an agreement between MATTR Limited (**we**, **us**, or
**our**) and you or the entity you represent ( **you** or **your**).
This Agreement takes effect when you click an “I Accept” button or tick box presented alongside
these terms, or when you otherwise access or use any of the Services or Materials (**Effective
Date**).
By entering into this Agreement, you warrant and represent that:
1. you are lawfully able to enter into contracts (e.g. you are not a minor); and
2. if you are entering into this Agreement for an entity (e.g. a company, government agency or
other organisation), you are authorised to do so and have legal authority to bind that entity.
1) #### Use of the Services and Materials. [#use-of-the-services-and-materials]
1. **General.** You may access and use the Services and Materials only in accordance with this
Agreement. As part of this Agreement, Service Level Agreements (**SLAs**) and Service Terms
apply to certain Services. Service Terms may specify additional Fees, restrictions and
obligations in connection with such Services. You must comply with the terms of this
Agreement and all Laws applicable to your use of the Services and Materials.
2. **Your account.** To access the Services, you must have an Account associated with a valid
email address and a valid form of payment.
3. **Third-Party Components.** You may use Third-Party Components that are made available to you
on the Site or through the Services. Third-Party Components are governed by this Agreement
and any separate terms and conditions accompanying such Third-Party Components (which may
include separate fees and charges).
4. **Trial period.** We may supply Services on a trial basis. Your Account may be suspended at
the end of the trial period unless you upgrade your Account to continue using or accessing
the Services.
2) #### Your Systems and Third-Party Components. [#your-systems-and-third-party-components]
1. **Necessary access to Your Systems.** We may need you to provide us with access to any
interfaces, software, platforms, systems (including accounts, account credentials or
delegated access), or infrastructure that are not owned or operated by us in order to supply
a Service to you (**Your Systems**). If we need access to Your Systems in order to supply a
Service to you, we will request your prior written approval.
2. **Where you provide access.** Where you provide us with access to Your Systems:
1. you must obtain and maintain the necessary access for us on an ongoing basis (including
permissions and credentials) for as long as required by us;
2. we will undertake that access on your behalf (and you appoint us as your agent for this
purpose and consent to us representing to third parties that we are your agent for this
purpose);
3. it is solely your responsibility to ensure compliance with any third-party licence,
developer or other terms that apply to any part of Your Systems owned or operated by a
third party (e.g. Google Pay or Apple Wallet terms), including in connection with our
access to those parts of Your Systems and our provision of the Services using Your
Systems in accordance with this Agreement;
4. if any charges or fees apply for that access, we may require you to pay those charges and
fees before we supply the Service, or we may pay those charges and fees and recover them
from you as Fees (with a reasonable administrative charge if specified in the relevant
Service Terms); and
5. you will defend, hold harmless and indemnify us from and against any claim or action
brought by a third party, including all damages, liabilities, costs and expenses, and
lawyers’ fees on a solicitor and own-client basis) arising out of or in connection with
our use of or access to Your Systems in accordance with this Agreement.
3. **Where you do not provide access.** Where you don’t provide access to Your Systems and this
adversely impacts our ability to provide Services to you, we won’t be responsible for any
resulting failure, delay or other adverse impact to the provision of Services to you.
4. **Third-Party Components**
1. You acknowledge that some Services that we provide are developed or provided by third
parties and not by us ( **Third-Party Components**).
2. Certain Third-Party Components we provide are subject to additional Service Terms, and
may be subject to a third-party licence. As set out in the Service Terms, these are
provided on an ‘as-is’ basis without any warranty of any kind. If there is any conflict
between this Agreement and that separate licence, the separate licence will prevail with
respect of the Third-Party Components.
3. We, not the Third-Party Components provider, are responsible for the supply. The
Third-Party Components provider and its licensors provide no warranties, support or
indemnities in respect of the Third-Party Components.
3) #### Changes. [#changes]
1. **Services.** We may change or discontinue any of the Services from time to time, subject to
any SLA we have agreed with you.
2. **Critical changes.** We may make changes referred to in clause 3.1 without notice and
despite any SLA to:
1. address or avoid a security or Intellectual Property risk to us or the Services or
Materials;
2. avoid any violation of any Law; or
3. maintain the commercial viability, security and availability of the Services or
Materials.
3. **Service Level Agreements.** We may change, discontinue or add Service Level Agreements from
time to time in accordance with clause 17.
4) #### Data security and storage. [#data-security-and-storage]
1. **Data security.** Without limiting clause 15 or your obligations under clause 6, we will
implement reasonable measures designed to help you secure Your Content against unauthorised
access, interference, modification, loss, or disclosure.
2. **Data region.** Where we allow you to select from a number of available Regions, you may
choose the Region in which Your Content will be processed and stored as part of the Services.
You consent to the storage of Your Content in, and transfer of Your Content into, the Region
you have selected. To provide billing, administration and support services, we may use and
process your Account Information in the Region you have selected and in another Region where
our billing, administration and support services are located.
3. **Data access.** We will not access, use or disclose Your Content except as is necessary or
required to:
1. maintain or provide the Services and Materials;
2. comply with the Law or legal processes, or to exercise, establish or defend our legal
rights; or
3. mitigate potential negative implications of a disaster or security incident (as
determined by us) which may include transfer of your data to a different Region.
4. **Legally requested disclosure.** We will not disclose Your Content or Account Information
except as necessary to comply with the Law or legal processes, or to exercise, establish or
defend our legal rights. If we are allowed to, we will notify you of any request or proposal
to disclose your information.
5) #### Data use and privacy. [#data-use-and-privacy]
1. **Use of Data.** We respect you and your End Users’ privacy and take data protection
seriously. We will only use your Account Information in accordance with our Privacy Policy,
which describes in more detail how we deal with Account Information and the personal data of
End Users. You must ensure that you comply with all applicable data protection and privacy
laws, including by obtaining the consent of each End User for the collection, use, storage,
transfer, processing and disclosure of personal information, sensitive information or special
category data (as those terms are defined in applicable data protection and privacy laws).
2. **Data Processing Terms.** Depending on the location of your End Users and other individuals
whose personal information will be collected, used, stored, transferred, processed or
disclosed as a result of you using or accessing the Services or Materials, our
[Data Processing Terms](/docs/resources/terms/data-processing-terms) may apply.
3. **Anonymised statistical data.** When you use our Services, we may create anonymised
statistical data from your data and usage of our Services, including through aggregation.
Once anonymised, we may use it for our own purposes, such as to provide and improve our
Services, to identify any unacceptable use of our Services, to develop new services or
product offerings, to identify business trends, and for other uses we communicate to you.
4. **Data breach notifications.** If we think there has been unauthorised access to, or
disclosure of, personal information inside your Account, we will let you know and endeavour
to give you information about what happened. Depending on the nature of the unauthorised
access or disclosure, and the location of those affected, you may be required to assess
whether the unauthorised access or disclosure must be reported to those affected and/or a
relevant authority. We will rely on you to make this decision, because you will have the most
knowledge about the personal information stored in your Account.
6) #### Your responsibilities. [#your-responsibilities]
1. **Your accounts.** Except to the extent caused by our breach of this Agreement:
1. you are responsible for all activities that occur under your Account, regardless of
whether the activities are authorised by you or undertaken by you, your employees or a
third party (including your contractors, agents or End Users); and
2. we and our related companies are not responsible for unauthorised access, interference or
modification to your Account, or any unauthorised access, modification, loss or
disclosure of Your Content or personal information or other data in your Account.
2. **Your Content and use of the Services.** You must ensure that Your Content, your, and your
End Users’ use of Your Content, Services and Materials comply with this Agreement, all
applicable Laws and all restrictions described in the MATTR Content and on the Site, and any
other policy or terms referenced in or incorporated into this Agreement. You are solely
responsible for the development, content, operation, maintenance, and use of Your Content.
3. **Securing and protecting Your Content.** Except to the extent we have agreed in writing or
expressly committed as part of the Services, you are responsible for:
1. properly configuring and using the Services and Materials; and
2. taking reasonable and appropriate action to secure, protect and backup your Account and
Your Content in a manner that will provide appropriate security and protection (which
might include use of encryption to protect Your Content) from unauthorised access,
interference, modification, loss or disclosure and routinely archiving Your Content.
4. **Account log-In details and private keys.** Account log-in details and private keys
generated by the Services are for your internal use only. You are responsible for protecting
them against unauthorised use or disclosure and you must not sell, transfer or sublicense
them, except:
1. to the extent that we otherwise permit in writing (which we may permit subject to
conditions); and
2. that you may disclose your private key to your employees, agents and contractors
performing work on your behalf.
5. **End Users.** You are responsible for your End Users’ use of Your Content, the Services and
the Materials. You must ensure all End Users comply with the terms of this Agreement and
agree to terms that are consistent with this Agreement. We do not have to provide any support
or services to End Users unless we have a separate agreement with you or them for the
provision of such support or services or are required to support them by Law (e.g. with
access to personal information).
7) #### Unacceptable use. [#unacceptable-use]
1. **Overview.** You must not, and must ensure that End Users will not, use the Services or
Materials in any manner or for any purpose other than as expressly permitted by this
Agreement. The examples described in this clause are not exhaustive. If you do not comply,
and ensure your End Users comply, with this clause, we may suspend or terminate your access
to and use of the Services in accordance with clauses 10 or 12 as appropriate.
2. **Illegal, harmful, or offensive use or content.** You must not use, or encourage, promote,
facilitate or instruct others to use, the Services or Materials in a manner that is illegal,
harmful, fraudulent, offensive or that infringes on the right of any person, or to transmit,
store, display, distribute or otherwise make available content that we consider to be
illegal, harmful, fraudulent, or offensive.
3. **Malware.** You must not use the Services or Materials to host or distribute any Content or
other computer technology that may damage, interfere with, surreptitiously intercept, or
expropriate any system, program, or data (including viruses, Trojan horses, worms, time
bombs, cancelbots and other malware).
4. **Unauthorised access.** You must not access or use the Services or Materials to threaten,
attempt to, or engage in conduct that would violate the security or integrity of (including
though a malicious act), or otherwise gain unauthorised access to, any communication,
network, computer or communications system, software application, or network or computing
device.
5. **Network interference.** You must not threaten, attempt, or engage in, conduct that is
likely to interfere with, pose a security risk to, or adversely impact our systems, Services,
Materials or Site, including the Content of our customers and their use of our Services,
Materials or Site. For clarity, this includes making network connections to any computer
system or network (unless you have permission to do so), monitoring or crawling systems or
networks in a way that impairs or disrupts them, denial of service attacks, intentionally
interfering with any computer system or network, or using any means to avoid usage
limitations and restrictions placed on the Services, Materials, Site or any other computer
system or network.
6. **License restrictions.** You must not breach the licence restrictions set out in clause
13.4.
7. **Monitoring and enforcement.** We may, but are not obliged to, investigate any violation of
this clause or any other misuse of the Services or Materials. We may report any activity that
we suspect violates any Law to law enforcement officials, regulators, or other appropriate
third parties. Our reporting may include disclosing Account Information. We also may
cooperate with law enforcement agencies, regulators, or other appropriate third parties to
help with the investigation and prosecution of illegal conduct by providing network and
systems information related to alleged violations of this clause.
8) #### Fees and payment. [#fees-and-payment]
1. **Fees.** We calculate and charge or invoice Fees Monthly or as otherwise agreed. You must
pay us the Fees for use of the Services and Materials by the due date for payment and using
one of the payment methods we have approved for your Account. You must pay all Fees payable
under this Agreement without any set-off, counterclaim, deduction or other withholding. We
may charge you interest at the Overdue Interest Rate on unpaid Fees from the date such unpaid
Fees became due until the date payment is received into our account in cleared funds.
However, if the Overdue Interest Rate exceeds the maximum permitted legal interest rate, the
interest chargeable will be reduced to reflect the maximum permitted legal interest rate.
2. **Changes to Fees.** Fees and charges for new Services, Materials or service features will be
effective when we post updated Fees on the Site or notify you of the Fees, or such other date
as we expressly state. We may increase or add new Fees for existing Services and Materials at
any time by giving you at least 30 days’ prior notice.
9) #### Taxes. [#taxes]
1. **Fees exclusive of Taxes.** Our Fees exclude Goods and Services Taxes and all other Taxes.
If we are liable for any Goods and Services Taxes or any other Tax in respect of the
provision of the Services or Materials or any other supply we make under this Agreement, you
will pay us, in addition to and at the same as our Fees, an amount equal to the amount of
such Tax, subject to receipt of a tax invoice (if applicable).
2. **Withholding Tax.** If:
1. you are required by Law to make any deduction or withholding from any amount payable to
us under this Agreement; or
2. we are required by Law to pay any Tax in relation to any amount receivable by us under
this Agreement, you must pay to us such additional amounts as are necessary so that,
after making the deduction, withholding or payment, the net amount received and retained
by us is equal to the amount we would have received and retained had no such deduction,
withholding or payment been made.
3. **Information.** If you deduct or withhold any amount from a payment to us under clause 9.2,
you will, at our request, provide us with reasonable evidence of payment of the deduction or
withholding to the relevant tax authority.
10) #### Suspension. [#suspension]
1. **General.** We may suspend your right to access or use any or all Services and Materials
immediately upon notice to you to the extent that we determine:
1. your or your End User’s use of the Services or Materials:
1. is in breach of clauses 5, 6, or 7;
2. could subject us, our related companies, or any third party to liability;
3. could be fraudulent; or
4. is prohibited under clause 19.6;
2. you are in breach of this Agreement;
3. you are in breach of your payment obligations under clause 8 in respect of any Service or
Materials; or
4. you have ceased to operate in the ordinary course of business, made an assignment for the
benefit of creditors or similar disposition of your assets, or become the subject of any
insolvency, bankruptcy, reorganisation, liquidation, dissolution or similar proceeding.
2. **Effect of suspension.** If we suspend your right to access or use any or all Services or
Materials:
1. you remain responsible for all Fees you incur during the period of suspension; and
2. you will not be entitled to any Service Credits under the Service Level Agreements for
any period of suspension.
11) #### Term. [#term]
1. **Term.** This Agreement will start on the Effective Date and will remain in effect unless
terminated under clause 12.
12) #### Termination. [#termination]
1. **Termination without cause.** Unless we have agreed a committed contract term with you, you
can terminate this Agreement in respect of any or all Services and Materials for any reason
by closing your account for the relevant Services. To terminate in respect of all Services
and Materials, you must close your account for all Services for which we provide an Account
closing mechanism. We may also terminate this Agreement for any or all Services and Materials
for any reason by providing you with at least 30 days’ written notice for any reason except
to the extent we have agreed to a longer committed contract term with you.
2. **Termination for cause.** Either party may terminate this Agreement in respect of any
affected Services and Materials if the other party is in material breach of this Agreement
and the breach remains uncured for a period of 30 days from receipt of written notice by the
other party. You must close your Account for all affected Services for which we provide an
account closing mechanism no later than the date notified to you.
3. **Immediate termination by us.** We may also terminate this Agreement in respect of any or
all Services and Materials immediately upon notice to you:
1. to the extent we have the right to suspend under clause 10;
2. if our relationship with a third-party partner who provides software or other technology
we use to provide affected Services or Materials expires, terminates or requires us to
change the way we provide the software or other technology as part of the Services or
Materials;
3. to comply with any applicable Law or legal processes; or
4. if we are subject to any legal or regulatory changes that we consider make it technically
or commercially burdensome for us to continue providing the affected Services or
Materials to you.
4. **Effect of termination.** Upon the Termination Date:
1. except as provided in clause 12.5, all your rights under this Agreement with respect to
affected Services and Materials immediately terminate;
2. you remain responsible for all Fees you have incurred for affected Services and Materials
up until, and including, the Termination Date and are responsible for any Fees you incur
for those affected Services or Materials during the post-termination period described in
clause 12.5;
3. you will immediately return or, if instructed by us, destroy all Material provided in
connection with affected Services which are in your possession; and
4. clauses 6.1, 8, 12, 13 (except the licence granted to you in clause 13.3), 14, 15, 16,
19, and 20 will continue to apply in connection with the affected Services and Materials.
5. **Consequences of termination.** Unless we terminate your use of the Services or Materials
pursuant to clause 12.2:
1. we will not take action to remove Your Content from the Services as a result of the
termination for 30 days following the Termination Date; and
2. we may remove Your Content from the Services any time following that 30 day period unless
otherwise agreed with you in writing.
6. **Use after Termination Date.** For any use of the Services or Materials after the
Termination Date, your obligations under this Agreement will apply, and you will pay the
applicable Fees in accordance with clause 8.
13) #### Intellectual Property. [#intellectual-property]
1. **Your Content.** You own Your Content. When you transfer Your Content into our Services, you
grant us a non-exclusive, transferable and sublicensable licence to use, copy, communicate,
transmit, store, analyse, adapt and back up all transferred data to provide the Services and
Materials to you and your End Users.
2. **Adequate rights.** You represent and warrant to us that:
1. you or your licensors own all right, title, and interest in and to Your Content and
Feedback, including to Intellectual Property;
2. you have all rights in Your Content and Feedback necessary to grant the rights
contemplated by this Agreement; and
3. none of Your Content and End Users’ use of Your Content or the Services, Materials or
Site will breach clause 5, 6, or 7, or otherwise breach any applicable third-party
licence, developer or other terms that apply in connection with Your Systems (e.g. Google
Pay or Apple Wallet terms).
3. **Your licence to use our Services.** We or our licensors own all rights, title, and interest
in and to the Services, Site and Materials, including to Intellectual Property. Subject to
the terms of this Agreement, we grant you a limited, revocable, non-exclusive,
non-transferrable licence for the Term of this Agreement to access and use the Services and
Materials solely in accordance with this Agreement, including subject to clauses 6.2 and 7.
The Services and Materials are not sub-licensable except to the extent set out in applicable
Service Terms or as otherwise notified to you. Except as provided in this clause 13.3, you
obtain no rights under this Agreement from us, our related companies or our licensors to the
Services or Materials, including any Intellectual Property.
4. **Licence restrictions.** You must not, and must not permit End Users to, access or use the
Services or Materials in any manner or for any purpose other than as expressly permitted by
this Agreement. Neither you nor any End User will, or will attempt to:
1. modify, distribute, alter, tamper with, repair, or otherwise create derivative works of
any Content included in the Services or Materials (except to the extent Content is
provided to you under a separate licence that expressly permits the creation of
derivative works);
2. reverse engineer, disassemble, or decompile the Services or Materials or apply any other
process or procedure to derive the source code of any software included in the Services
or Materials (except to the extent applicable Law doesn’t allow this restriction);
3. access or use the Services or Materials in a way intended to avoid incurring fees, bypass
usage limits or bypass quotas; or
4. resell or sublicense the Services or Materials except with our prior written agreement.
[Contact us](mailto:info@mattr.global) to discuss your implementation and
requirements.
5. **MATTR Marks.** You may only use the MATTR Marks with our prior written permission and in
accordance with any trademark usage guidelines that we have published on the Site or
otherwise notified to you.
6. **Feedback.** You are not obliged to provide Feedback to us or our related companies. If you
do so:
1. we and our related companies may use, modify or develop the Feedback for any purpose
without restriction and without attribution or compensation to you or any other person;
and
2. you irrevocably assign to us all right, title, and interest in and to the Feedback and
agree to provide us any assistance we require to document, perfect, and maintain our
rights in the Feedback.
14) #### Indemnity. [#indemnity]
1. **General.** You will defend, indemnify, and hold harmless us, our related companies and
licensors, and each of our and their respective employees, officers, directors, and
representatives from and against any third-party claim or Losses arising out of or in
connection with:
1. your or any End Users’ access to or use of the Services or Materials (including any
activities under your Account and use by your employees, agents or contractors);
2. breach of this Agreement or violation of applicable Laws by you, End Users or Your
Content; or
3. a dispute between you and any End User. You will reimburse us for reasonable legal fees,
as well as our employees’, contractors’ and agents’ time and materials spent responding
to any third party subpoena or other compulsory legal order or process associated with
third party claims described in this clause at our then-current hourly rates.
2. **The Services.** Subject to the limitations in this clause 14, we will defend you and your
employees, officers, and directors against any third-party claim alleging that the Services
infringe or misappropriate that third party’s Intellectual Property, and will pay the amount
of any adverse final judgment or any settlement agreed by us.
3. **Your Content.** Subject to the limitations in this clause 14, you will defend us, our
related companies, and our and their respective employees, officers, and directors against
any third-party claim alleging that any of Your Content infringes or misappropriates that
third party’s Intellectual Property, and will pay the amount of any adverse final judgment or
settlement agreed by you.
4. **Exclusions.** We will have no obligations or liability to you under clause 14.2 arising out
of or in connection with:
1. modification of the Services or Materials by anyone other than us or our related
companies;
2. combination, operation or use of the Services or Materials with any other goods,
software, product, data or services not provided by us; or
3. your or any End User’s use of the Services or Materials after we have notified you to
discontinue such use. The remedies provided in this clause 14 are the sole and exclusive
remedies for any third-party claims of infringement or misappropriation of Intellectual
Property by the Services or by Your Content.
5. **Remediation.** For any claim covered by clause 14.2 we will, at our option, either:
1. procure the rights to use that portion of the Services alleged to be infringing;
2. replace the alleged infringing portion of the Services with a non-infringing alternative;
3. modify the alleged infringing portion of the Services to make it non-infringing; or
4. terminate the allegedly infringing portion of the Services or this Agreement or both (as
applicable).
6. **Process.** The party seeking defence or indemnity under this clause 14 must:
1. give the other party prompt written notice of the claim;
2. not make any admission and must not purport to settle the claim without the other party’s
prior written consent;
3. permit the other party to control the defence and settlement of the claim; and
4. reasonably cooperate with the other party (at the other party’s expense) in the defence
and settlement of the claim.
15) #### Warranties. [#warranties]
1. **Exclusion of warranties.** To the maximum extent permitted by Law, the Services are
provided on an “as-is” and “as-available” basis with no warranties other than those expressly
set out in this Agreement. Except to the extent prohibited by Law (or to the extent any
statutory rights apply that cannot be excluded, limited or waived), we and our related
companies and licensors:
1. make no representations or warranties of any kind, whether express, implied, statutory or
otherwise regarding the Services or Third-Party Components; and
2. disclaim all warranties, including any implied or express warranties:
1. of merchantability, satisfactory quality, fitness for a particular purpose,
non-infringement, or quiet enjoyment;
2. arising out of any course of dealing or usage of trade;
3. that the Services, Material or Third-Party Components will be uninterrupted, error
free or free of harmful components; and
4. that any Content will be secure or not otherwise lost or altered. However, see clause
19.15.
16) #### Liability. [#liability]
1. **Limitation.** Subject to clauses 16.2 and 16.3, and except for payment obligations under
clause 14.2, in no case will our and our related companies’ and licensors’ aggregate
liability under this Agreement exceed the amount you actually pay us under this Agreement for
the Service or Materials that gave rise to the claim during the 12 months before the
liability arose.
2. **Exclusion.** We and our related companies will not be liable to you or any other person for
any compensation, reimbursement, or damages:
1. for loss of profits, revenues, customers, opportunities, goodwill, use, or data or for
any indirect, incidental, special, consequential or exemplary damages, even if we have
been advised of the possibility of such damages;
2. arising out of or in connection with your inability to use the Services or Materials as a
result of:
1. this Agreement or your use of or access to the Services or Materials being terminated
or suspended for any reason,
2. us discontinuing of some or all of the Services or Materials in whole or in part, or
3. downtime or unavailability of any part of the Services or Materials, except as
expressly stated in our
[Service Level Agreement](/docs/resources/terms/service-level-agreement) ; or
3. arising out of or in connection with your inability to use the Services or Materials in
connection with:
1. the cost of you procuring substitute goods or services;
2. investments, expenditures, or commitments by you in connection with this Agreement or
your use of or access to the Services or Materials;
3. any unauthorised access to, alteration of, or the deletion, destruction, damage, loss
or failure to store any of Your Content or other data that arises out of or in
connection with your acts, omissions or breach of this Agreement. However, see clause
19.15.
3. **Recovery of data.** Our liability for loss of or damage to Your Content is limited to
taking reasonable steps to recover (if practicable) the affected data from our available
backups.
4. **Application of Law.** The exclusions and limitations set out in this clause 16 apply only
to the maximum extent permitted by applicable Law. See clause 19.15.
17) #### Changing this Agreement. [#changing-this-agreement]
1. **Changes.** Subject to clause 17.2, we may change this Agreement from time to time by
publishing an updated version of the relevant document on the Site or notifying an update to
you in accordance with clause 19.10. Any such changes will come into effect 31 days after the
updated version is published or notified (or the earlier of the two if the change is both
published and notified). If the changes have a material detrimental impact, you may terminate
this Agreement before the changes come into effect under clause 12.1.
2. **Service Level Agreement changes.** For any changes to a Service Level Agreement, we will
provide you with at least three months’ advance notice in accordance with clause 19.10. If
the changes have a material detrimental impact, you may terminate this Agreement under clause
12.1 before the changes come into effect.
3. **Application of changes.** By continuing to use the Services or Materials after the
effective date of any changes to this Agreement, you agree to be bound by the modified terms.
Please check the Site regularly for changes to this Agreement. The date on which we last
changed each part of this Agreement is the date listed at the top of the relevant document
forming part of the Agreement.
18) #### Disputes. [#disputes]
1. **Addressing your concerns.** Most concerns can be resolved quickly and to everyone’s
satisfaction by contacting our support team. If we are unable to resolve your complaint to
your satisfaction (or if we haven’t been able to resolve a dispute we have with you after
attempting to do so informally), you and we agree to resolve those disputes through binding
arbitration in accordance with clause 18.2.
2. **Arbitration.** Any dispute or claim arising out of or in connection with this Agreement or
the Services will be resolved by binding arbitration using a sole arbitrator under the
Arbitration Act 1996 (NZ). The arbitration will be conducted in Auckland, New Zealand using
the English language in accordance with clause 19.5. If the parties cannot agree on an
arbitrator within five Working Days of a party issuing an arbitration notice, the arbitrator
will be appointed at the request of either party by the president for the time being of the
New Zealand Law Society or his or her nominee. Clauses 3 (Powers relating to conduct of
arbitral proceedings) and 6 (Costs and expenses of an arbitration) of the Second Schedule of
the Arbitration Act 1996 will apply (but no other clauses in the Second Schedule will apply).
3. **Urgent relief.** Nothing in this Agreement will prevent either party seeking or obtaining
any order or relief by way of injunction or declaration or other equitable or statutory
remedy against the other party where that party believes such order or relief is necessary
for the urgent protection of its rights or property, including Intellectual Property.
4. **Conduct of claims and litigation.** We and you agree that any dispute resolution
proceedings will be conducted only on an individual basis and not in a class, consolidated or
representative action. If for any reason a claim proceeds in court rather than in
arbitration, we and you waive any right to a jury trial.
19) #### General. [#general]
1. **Assignment.** You must not assign or otherwise transfer this Agreement or any of your
rights and obligations under this Agreement, except with our prior written consent. Any
assignment or transfer in violation of this clause 19 will be void. We may novate, assign or
otherwise transfer this Agreement without your consent:
1. in connection with a merger, acquisition or sale of all or substantially all of our
assets; or
2. to any related company or as part of a corporate reorganisation, and in the event of a
novation or transfer, effective upon the novation or transfer, the new party nominated by
us is deemed substituted for us as a party to this Agreement and we are fully released
from all of our obligations and duties to perform under this Agreement. Subject to the
foregoing, this Agreement will be binding upon, and inure to the benefit of the parties
and their respective permitted successors and assigns.
2. **Entire agreement.** This Agreement is the entire agreement between you and us regarding its
subject matter. It supersedes all prior or contemporaneous representations, understandings,
agreements, or communications between you and us, (whether written or verbal) regarding the
subject matter of this Agreement. We will not be bound by any term, condition or other
provision that is different from or in addition to the provisions of this Agreement (whether
or not it would materially alter this Agreement, and whether or not they are prior to,
contemporaneous with, or subsequent to this Agreement) including terms contained in your
purchase orders, receipts, confirmations, RFx documentation and other standard terms.
3. **Priority.** Where there is any conflict or ambiguity between this document and any other
document, Service Terms or Service Level Agreement forming part of this Agreement, the
following descending order of priority will apply:
1. applicable Service Terms;
2. this Customer Agreement document;
3. applicable Service Level Agreements.
4. **Force majeure.** We and our related companies will not be liable for any delay or failure
to perform any obligation under this Agreement where the delay or failure results from any
cause beyond our reasonable control, including acts of God, labour disputes or other
industrial disturbances, electrical or power outages, utilities or other telecommunications
or cloud infrastructure failures, earthquake, storms or other elements of nature, epidemics,
pandemics, blockages, embargoes, riots, acts or orders of government, acts of terrorism, or
war.
5. **Governing Law and jurisdiction.** This Agreement will be governed by the Laws of New
Zealand and you consent to the non-exclusive jurisdiction of the New Zealand courts. You must
not object to the transfer of any proceedings to New Zealand courts on any basis, including
inconvenience. The parties agree that United Nations Convention on Contracts for the
International Sale of Goods does not apply to this Agreement.
6. **Export control.** Each party will comply with all applicable export control laws and
regulations. You are solely responsible for your compliance with applicable laws in relation
to how you choose to use the Services, including the transfer and processing of Your Content,
the provision of Your Content to End Users, and the Region in which any of the foregoing
occur. You must not use our Services in violation of any export or trade embargo laws that
apply to you. You represent and warrant that you are not in a jurisdiction subject to
sanctions that would affect our provision of the Services or Material or otherwise designated
on any list of prohibited or restricted parties maintained by the United Nations Security
Council, the European Union or its Member States, the United States, New Zealand or other
applicable government authority.
7. **Our relationship with you.** We and you are independent contractors, and this Agreement
will not be construed to create a partnership, joint venture, agency, or employment
relationship. Neither party, nor any of their respective related companies, or personnel is
an agent of the other for any purpose or has the authority to bind the other.
8. **Language.** All communications and notices made or given pursuant to this Agreement must be
in the English language. If we provide a translation of the English language version of this
Agreement, the English language version of the Agreement will control if there is any
conflict.
9. **Confidentiality and publicity.** You may use MATTR Confidential Information only in
connection with your use of the Services and Materials as permitted under this Agreement. You
must not disclose MATTR Confidential Information without our written agreement. You must take
all reasonable measures to avoid disclosure, dissemination or unauthorised use of MATTR
Confidential Information, including, at a minimum, those measures you take to protect your
own confidential information of a similar nature. You must not issue any press release or
make any other public communication with respect to us, this Agreement or the Services or
Materials except with our prior written approval.
10. **Giving you notices.** We may provide notice to you under this Agreement by sending a
message to the email address associated with your account, or by posting a visible notice on
the Site or through the Services. Notices we provide by posting on the Site will be
effective upon posting, and notices we provide by email will be effective when we send the
email. It is your responsibility to keep your email address current. You will be deemed to
have received any email sent to the email address then associated with your account when we
send the email, whether or not you actually receive the email and whether or not we receive
a “bounce-back” or other notice of non-delivery.
11. **Giving us notices.** Any notice you send to us must be sent to [accounts@mattr.global](mailto:accounts@mattr.global) and
will be effective upon receipt into that inbox, provided that if received after 5 PM (NZST
or NZDT, as applicable) on a Working Day, they will be deemed received at 9 AM (NZST or
NZDT, as applicable) on the next Working Day.
12. **Third-Party beneficiaries.** The terms set out in clause 14 relating to indemnified third
parties and the references in this Agreement to our related companies and licensors are
expressed for the benefit of that person for the purposes of the Contract and Commercial Law
Act 2017 (Part 2, Subpart 1). Otherwise, this Agreement does not create any third-party
beneficiary rights in any individual or entity that is not a party to this Agreement.
13. **No waiver.** The failure by us to enforce any provision of this Agreement will not
constitute a present or future waiver of such provision nor limit our right to enforce such
provision or any other provision at a later time. All waivers by us must be in writing to be
effective.
14. **Severability.** If any portion of this Agreement is held to be invalid or unenforceable,
the remaining portions of this Agreement will remain in full force and effect. Any invalid
or unenforceable portions will be interpreted to give effect to the intent of the original
portion. If such construction is not possible, the invalid or unenforceable portion will be
severed from this Agreement, but the rest of the Agreement will remain in full force and
effect.
15. **Consumer Laws.** In some places, like New Zealand and Australia, there may be
non-excludable warranties, guarantees or other rights provided by Law ( **Non-excludable
Consumer Guarantees**). They still apply – these terms do not exclude, restrict or modify
them. Except for such Non-excludable Consumer Guarantees and other rights you have that we
cannot exclude, we expressly exclude all warranties and guarantees and we are only bound by
the express terms set out in this Agreement. Our liability for breach of a Non-excludable
Consumer Guarantee is limited, at our option (and subject to clause 16.1), to either
re-performing, refunding, replacing or paying the cost of replacing the relevant service
(unless the Non-excludable Consumer Guarantee says otherwise).
20) #### Definitions and interpretation. [#definitions-and-interpretation]
1. **Definitions.** In this Agreement, the following terms will have the meaning set out below:
**Account** means the account, and associated log-in details that you’ll need to use in
order to access the Services.
**Account Information** means:
1. information about you that you provide to us in connection with the creation or
administration of your Account. For example, names, usernames, phone numbers, email
addresses and billing information associated with your Account; and
2. usage data related to your Account, such as resource identifiers, metadata tags, security
and access roles, rules, usage policies, permissions, usage statistics and analytics.
**Agreement** means this Customer Agreement document, together with the applicable Service
Terms and Service Level Agreements.
**API** means an application programming interface.
**Content** means software (including machine images), data, metadata, documents, text,
audio, video, images and other materials.
**Data Processing Terms** means the document with that title which we provide to you or make
available on the Site or through the Services, as may be updated by us from time to time.
**Documentation** means the user guides and admin guides (in each case exclusive of content
referenced via hyperlink) for the Services located at [https://learn.mattr.global](https://learn.mattr.global) (or any
updated URL designated by us), as such user guides and admin guides may be updated by us
from time to time.
**Effective Date** means the date when you accept the Customer Agreement, or otherwise
access or use any of the Services or Materials, whichever is earlier.
**End User** means your customers and any other individual or entity that directly or
indirectly:
1. accesses or uses Your Content; or
2. otherwise accesses, uses or benefits from the Services or Materials under your Account or
through services you provide to them.
**Feedback** means all ideas, suggestions and other feedback that you provide to us.
**Fees** means all amounts owing under this Agreement for the Services and Materials, as
described on the Site or otherwise notified by us to you at the time you order those
Services and Materials.
**Goods and Services Taxes** includes New Zealand goods and services Tax chargeable in
accordance with the Goods and Services Tax Act 1985 and any goods and services Tax, value
added Tax or sales Tax imposed under the laws of any other jurisdiction.
**Intellectual Property** means all intellectual property rights, including:
1. patents, designs, trademarks, service marks, copyright material or works, registered
designs, database rights, domain name rights, trade names, symbols and logos (whether
registered or unregistered); and
2. all formulae, methods, plans, data, drawings, specifications, characteristics,
algorithms, source and object code, equipment, designs, inventions, discoveries,
improvements, know-how, software, trade secrets and other proprietary information.
**Law** or **Laws** means:
1. any statute, regulation, bylaw, ordinance or subordinate legislation;
2. any binding court order, judgment or decree;
3. any binding order of any other governmental body; and
4. any applicable industry code, convention, policy or standard enforceable by law.
**Losses** means any claims, damages, losses, liabilities, costs, and expenses (including
reasonable lawyers’ fees).
**Material** means the MATTR Content and the MATTR Marks. Material does not include any
Third-Party Components.
**MATTR Confidential Information** means all non-public information disclosed by us, our
related companies, business partners or our or their respective employees, contractors or
agents that is designated as confidential or that, given the nature of the information or
circumstances surrounding its disclosure, reasonably should be understood to be
confidential. MATTR Confidential Information includes:
1. non-public information relating to our or our related companies or business partners’
goods, services, software, technology, processes, systems, customers, business plans,
promotional and marketing activities, finances and other business affairs;
2. third-party information that we are obliged to keep confidential; and
3. the nature, content and existence of any discussions or negotiations between you and us
or our related companies.
MATTR Confidential Information does not include any information that:
1. is or becomes publicly available without breach of this Agreement;
2. can be shown by documentation to have been known to you at the time of your receipt from
us;
3. is received from a third party who did not acquire or disclose the same by a wrongful or
tortious act; or
4. can be shown by documentation to have been independently developed by you without
reference to the MATTR Confidential Information.
**MATTR Content** means Content we or any of our related companies make available in
connection with the Services or on the Site to allow access to and use of the Services,
including APIs, WSDLs, SDKs, Documentation, sample code, software libraries, command line
tools, proofs of concept, templates and other related technology (including any of the
foregoing that are provided by our personnel). MATTR Content does not include the Services
or Third-Party Components.
**MATTR Marks** means any trademarks, service marks, service or trade names, domain names,
logos, and other designations of us and our related companies that we may make available to
you in connection with this Agreement.
**Month** or **Monthly** means the period from a day of one month to the corresponding day
of the next month, or if such does not exist, the last day of the next month.
**Overdue Interest Rate** means the “Business Overdraft Base Rate” of ANZ Bank New Zealand
Limited ( **Base Rate**) plus 1.5% per annum (or, if the Base Rate ceases to be published,
such alternative rate as notified by us from time to time).
**Privacy Policy** means the privacy policies located on the Site (and any successor or
related locations designated by us), as may be updated by us from time to time.
**Region** means the geographical location where we will store and process Your Content as
part of the Services. We may also enable multiple geographic locations and allow you to
select from such locations for the storage and processing of Your Content.
**Service** means our platform, software offerings, or capabilities and any other services
made available by us or our related companies under this Agreement (however accessed and
including their presentation on the Site).
**Service Terms** means, in respect of a Service, the terms and conditions that apply to
your use of that Service as notified by us to you or published on our Site.
**Service Credit** means the dollar credit that we may credit back to an eligible Account in
accordance with a Service Level Agreement.
**Service Level Agreement** means a service level agreement for the relevant Services, which
we provide to you or otherwise set out on the Site (or any updated URL designated by us) as
may be updated by us from time to time.
**Site** means our website at mattr.global and learn.mattr.global as may be updated by us
from time to time, or any derivative, related or new site on which we offer our Services or
you can access our Materials.
**Site Terms** means the terms of use that relate to any use of the Site, located on the
Site (or any updated URL designated by us), as may be updated by us from time to time.
**Tax** or **Taxes** includes any present or future tax, levy, impost, duty, rate, charge or
fee imposed or levied by any government authority, whether in New Zealand or elsewhere,
together with any related interest, penalties or charges, but does not include a tax imposed
on or calculated by reference to overall net income.
**Term** means the term of this Agreement, as set out in clause 11.
**Termination Date** means the effective date of termination provided in accordance with
clause 12.
**Third-Party Components** has the meaning set out in clause 2.2.
**Working Days** means any day other than Saturday, Sunday or a public holiday observed in
Auckland, New Zealand.
**Your Content** means Content that you or your End Users transfer to us for processing,
storage or use in connection with the Services under your Account and any computational
results that you or any End User derive from the foregoing through their use of the
Services. For clarity, Your Content includes Content that you or any End User stores with us
in connection with our Services and Materials. Your Content does not include Account
Information.
**Your Systems** has the meaning given in clause 2.1.
2. **Interpretation.** In this Agreement:
1. a reference to any document, enactment or regulation includes a reference to that
document as amended or replaced from time to time;
2. headings appear as a matter of convenience and do not affect the meaning or construction
of the Agreement;
3. the word “includes” or “including” and similar terms do not limit the meaning of
preceding words;
4. a reference to any monetary amount is a reference to United States dollars;
5. a reference to a person includes a corporation sole and also a body of persons, whether
corporate or unincorporated;
6. the singular includes the plural and vice versa;
7. words importing one gender include the other genders; and
8. any rule of law or legal decision that would require interpretation of this Agreement
against the party that drafted it is not applicable and is hereby waived.
## Previous versions [#previous-versions]
[MATTR Customer Agreement - 20 April 2022 (archived)](/docs/resources/terms/customer-agreement/20-4-22)
[MATTR Customer Agreement - 25 March 2021 (archived)](/docs/resources/terms/customer-agreement/25-3-21)
# MATTR Customer Agreement
URL: /docs/resources/terms/customer-agreement
Last Updated: 21 April 2026
[See what's changed](/docs/resources/terms/customer-agreement/recent-changes) |
[Previous versions](#previous-versions)
This Agreement sets out the terms and conditions governing your access to and use of the Services and Materials (as defined below). It is an agreement between MATTR Limited (we, us, or our) and you or the entity you represent (you or your).
This Agreement takes effect when you click an “I Accept” button or tick box presented alongside these terms, or when you otherwise access or use any of the Services or Materials (Effective Date).
By entering into this Agreement, you warrant and represent that:
1. you are lawfully able to enter into contracts (e.g. you are not a minor); and
2. if you are entering into this Agreement for an entity (e.g. a company, government agency or other organisation), you are authorised to do so and have legal authority to bind that entity.
1) #### Definitions and interpretation. [#definitions-and-interpretation]
1. **Definitions.** In this Agreement, the following terms will have the meaning set out below:
**Account** means the account, and associated log-in details, you use to access the Services or Materials.
**Affiliate** means an entity controlled by a party, which controls a party or is under common control with a party. As used in this definition “control” and its variants means ownership of more than 50% of the voting equity of an entity.
**Agent** has the meaning given to it in clause 4.4.
**Agreement** means this Customer Agreement, together with all Order Forms and applicable Service Terms, Support Terms and Data Processing Terms.
**API** means an application programming interface.
**Confidential Information** means information including Materials disclosed by one party to the other party under or in connection with this Agreement, including the terms of this Agreement (which is confidential to MATTR), the Fees, any information about the disclosing party’s products, pricing or business plans, and any other information that:
1. is marked confidential;
2. the discloser advises is confidential at or around the time the information is communicated to the recipient;
3. the recipient knows or ought to know is confidential; or
4. is by its nature confidential,
but excludes information that:
5. is in the public domain, other than by breach of this Agreement;
6. is in the possession of the recipient without breach of confidentiality by the recipient or other person; or
7. is independently developed by the recipient.
**Content** means software (including source and object code, machine images, code snippets and samples), data, metadata, instructions, reports, documents, text, audio, video, images and other materials.
**Copyleft Licence** means any version of the GNU General Public Licence (“GPL”), including GPL v2, GPL v3, the AGPL and LGPL, or other open source licence terms that contain equivalent copyleft provisions to the GPL.
**Customer Agreement** means clauses 1-21 of this MATTR Customer Agreement, excluding the Order Form and Service Terms.
**Customer Application User** means an individual user of applications you create, implement or operate using the Services or Materials, including:
1. onboarding or issuance portals, digital wallet or credential verifier apps created by, using or dependent upon our platform, APIs and/or software development kits (“SDKs”); and/or
2. Customised Apps or licensed MATTR apps provided for you as part of the Services or Materials.
**Customer Representative** means an individual (including any Agent) involved with your implementation or operation of the Services or Materials, including:
1. MATTR Users; and
2. other individuals whose information we process in connection with the maintenance or administration of the relationship between you and us.
**Customer Representative Information** means:
1. information about Customer Representatives that you provide us or is generated through the use of our Services or Materials. For example, names, usernames, phone numbers, email addresses and billing information associated with your Account; and
2. usage data related to your Account, such as resource identifiers, metadata tags, security and access roles, rules, usage policies, permissions, usage statistics and analytics.
**Customised App** means a white labelled version of a MATTR web or mobile application, as may be specified in an Order Form.
**Data Processing Terms** means the document with that title which we provide to you or make available on the Site or through the Services, as may be updated by us from time to time.
**Documentation** means the user guides and admin guides (in each case exclusive of content referenced via hyperlink) for the Services located at [https://learn.mattr.global](https://learn.mattr.global) (or any updated URL designated by us), as such user guides and admin guides may be updated by us from time to time.
**Effective Date** means the date when this Customer Agreement is agreed by both parties, or the date you otherwise access or use any of the Services or Materials, whichever is earlier.
**End User** means:
1. any individual acting on behalf of an Issuer or Verifier;
2. any Holder or Customer Application User; and/or
3. any other individual who directly or indirectly (i) uses Your Content or (ii) otherwise accesses, uses or benefits from the Services or Materials, including through services or materials you provide or support, and whether authorised by you or not, provided that End User does not include a Customer Representative in their role as a Customer Representative.
**Feedback** means all ideas, suggestions, feature or roadmap requests, comments and other feedback that you provide to us.
**Fees** means all amounts owing by you under this Agreement for the Services and Materials, as specified in an Order Form, including any:
1. fees calculated by reference to charges payable by us to third party suppliers;
2. fees calculated by reference to our standard rates or any daily or hourly rates specified in any Order Form; and
3. fixed fees or subscription charges.
**Good Industry Practice** means, in the performance of this Agreement, the exercise of the skill, diligence, prudence, foresight and judgment that would be expected from a skilled and experienced person engaged in such performance in the same or similar circumstances.
**Sales Taxes** includes any sales tax, value added tax or equivalent taxes imposed under the laws of any jurisdiction (including New Zealand or Australian Goods and Services Tax).
**Holder** means an individual who holds a verifiable credential processed directly or indirectly via the Services or Materials.
**Intellectual Property** means all intellectual property rights, including:
1. patents, designs, trademarks, service marks, copyright material or works, registered designs, database rights, domain name rights, trade names, symbols and logos (whether registered or unregistered); and
2. all formulae, methods, plans, data, drawings, specifications, characteristics, algorithms, source and object code, equipment, designs, inventions, discoveries, improvements, know-how, software, trade secrets and other proprietary information.
**Issuer** means a person or entity who issues verifiable credentials to a Holder directly or indirectly via the Services or Materials, including by using:
1. applications created by, using or dependent upon our platform, APIs and/or SDKs; and/or
2. applications or Customised Apps provided or licensed to you as part of the Services or Materials.
**Law or Laws** means:
1. any statute, regulation, bylaw, ordinance or subordinate legislation;
2. any binding court order, judgment or decree;
3. any binding order of any other governmental body; and
4. any applicable industry code, convention, policy or standard enforceable by law.
**Losses** means any claims, damages, losses, liabilities, costs, and expenses (including reasonable lawyers’ fees).
**Materials** means Content we or any of our Affiliates make available in connection with the Services or on the Site in connection with the Services, including APIs, SDKs, Customised Apps (other than Your Content included in such Customised Apps), Documentation, sample code, software libraries, command line tools, proofs of concept, prototypes, templates and other related technology (including any of the foregoing that are provided by our Personnel). Material does not include the Services.
**MATTR Marks** means any trademarks, service marks, service or trade names, domain names, logos, and other designations of us and our Affiliates that we may make available to you in connection with this Agreement.
**MATTR User** means authorised users of Services and/or Materials provided by MATTR to you or capability derived from the Services and Materials (e.g. your Agents, developers, product teams, security teams who use the MATTR portal, service desk portal, MATTR APIs, MATTR GO Hold, MATTR SDKs etc). MATTR User does not include an End User in their role as an End User.
**Milestone Date** means the target date for completion of a Milestone, as set out in an Order Form.
**Month or Monthly** means the period from a day of one month to the corresponding day of the next month, or if such does not exist, the last day of the next month.
**Open Source Component** means software provided by a third party and licensed on open source terms.
**Order Form** means an order for Services under this Agreement entered into by you and MATTR in one of the following ways:
1. by clicking to accept applicable details for an order for Services via an online process; or
2. by you and MATTR signing a written Order Form document.
Except for the purposes of clause 2.1 of this Customer Agreement, an Order Form includes and incorporates those Service Terms set out or referred to in it.
**Overdue Interest Rate** means the “Business Overdraft Base Rate” of ANZ Bank New Zealand Limited (Base Rate) plus 1.5% per annum (or, if the Base Rate ceases to be published, such alternative rate as notified by us from time to time).
**Package Terms** means Service Terms that apply across all or a specified collection of the Services set out in an Order Form.
**Personnel** means, in respect of a person, its agents, officers, employees and contractors.
**Privacy Policy** means the privacy policies located on the Site (and any successor or related locations designated by us), as may be updated by us from time to time.
**Region** means the geographical location where we will primarily store and process Your Content as part of the Services as specified in an Order Form.
**Service** means a service described in an Order Form and includes any Materials specified to be provided as part of the supply of that Service.
**Service Terms** means the terms and conditions that apply to your use of a Service as set out or referred to in your Order Form. Service Terms may be Package Terms or specific to individual Services and include all Specifications or other documents linked to or incorporated by reference in the Service Terms.
**Service Credit** means the dollar credit that we may credit back to an eligible Account in accordance with agreed Support Terms.
**Site** means our website at mattr.global and learn.mattr.global as may be updated by us from time to time, or any derivative, related or new site on which we offer our Services or you can access our Materials.
**Site Terms** means the terms of use that relate to any use of the Site, located on the Site (or any updated URL designated by us), as may be updated by us from time to time.
**Specification** means a description of a Service we will provide to you. A Specification may be linked to from the Service Terms for a Service and may be described as a Product Specification or Service Specification.
**Start Date** means the commencement date for a Service, as specified in an Order Form.
**Support Terms** means the Support Terms and Conditions specified or linked to in an Order Form. The Support Terms detail the Service Level Agreement that applies to our Services and/or Materials.
**Tax or Taxes** includes any present or future tax, levy, impost, duty, rate, charge or fee imposed or levied by any government authority in any country, together with any related interest, penalties or charges, but does not include a tax imposed on or calculated by reference to overall net income.
**Term** means the period for which we will provide Services and/or Materials to you, as specified in an Order Form.
**Termination Date** means the date that this Agreement or an Order Form terminates (as applicable) in accordance with clause 14 or the terms set out in the applicable Order Form (as the context requires).
**Third Party Credential Capability Provider** means (i) a smart phone platform provider (“OEM Wallet”) with a native wallet application for storing digital credentials; (ii) third party providers of trust list information or security certificate verification details for digital credentials; (iii) third party providers of services enabling Verifiers access to an OEM Wallet; and (iv) other third parties providing services, software, data or inputs that we designate as a Third Party Credential Capability Provider in an Order Form.
**Trial Access** means where we agree with you that we are providing you with access to certain of our Services and/or Materials for trial use only (including where you sign up for a free MATTR VII account on our website, or where you receive free trial access to our SDKs).
**Verifier** means a person or entity who verifies credentials directly or indirectly using the Services or Materials, including by using:
1. applications created by, using or dependent upon our platform, APIs and/or SDKs; and/or
2. applications or Customised Apps provided or licensed to you as part of the Services or Materials.
**Working Days** means any day other than Saturday, Sunday or a public holiday observed in Auckland, New Zealand.
**Your Marks** means any trademarks, service marks, service or trade names, domain names, logos, and other designations of you or your Affiliates.
**Your Content** means Content that you or End Users transfer to us for processing, storage or use in connection with the Services under your Account and any computational results derived through use of the foregoing via the Services or Materials. For clarity, Your Content includes Content that you or any End User stores with us in connection with our Services and Materials. Your Content does not include Customer Representative Information.
**Your Systems** means interfaces, software, platforms, systems, cloud services, infrastructure or distribution channels that are not owned or operated by us.
2. **Interpretation.** In this Agreement:
1. a reference to any document, enactment or regulation includes a reference to that document as amended or replaced from time to time;
2. headings appear as a matter of convenience and do not affect the meaning or construction of the Agreement;
3. the word “includes” or “including” and similar terms do not limit the meaning of preceding words;
4. a reference to a person includes a corporation sole and also a body of persons, whether corporate or unincorporated;
5. the singular includes the plural and vice versa;
6. words importing one gender include the other genders; and
7. any rule of law or legal decision that would require interpretation of this Agreement against the party that drafted it is not applicable and is hereby waived.
2) #### Agreement. [#agreement]
1. **Structure of Agreement.** The terms and conditions of this Customer Agreement apply to all Services we provide to you. The contract between you and us in respect of any Services is made up of:
1. one or more Order Forms (including, for clarity, Order Forms that have been accepted by you through an online ordering process), referencing this Customer Agreement;
2. Service Terms referred to in, or incorporated into, an Order Form;
3. other documents linked to, or incorporated by reference in, the Order Form or Service Terms, such as Specifications;
4. this Customer Agreement, which applies to all Services to be supplied by MATTR to you; and
5. any applicable Support Terms and Data Processing Terms.
2. **Priority.** If there is any conflict or ambiguity between any document or part of document comprising this Agreement, they will be interpreted in descending order of priority as set out in clause 2.1. If there is any conflict or ambiguity between two documents given the same priority in clause 2.1, the later in time will be given priority.
3. **Order Forms.**
1. Service(s) and/or Materials to be provided under this Agreement will be set out in Order Forms. An Order Form may include additional terms and conditions and will incorporate the Service Terms that apply to each relevant Service.
2. The parties may agree to add further Services or Materials under this Customer Agreement from time to time, by entering into one or more subsequent Order Forms, referencing this Customer Agreement.
3. Any services, software and/or hardware not expressly specified in an Order Form as being provided by MATTR are out of scope and will be subject to additional Fees on a time and materials basis at MATTR’s standard rates from time to time.
4. In addition to any Services and/or Materials purchased in an Order Form, we can provide you the following disengagement services on request, on a time and materials basis at MATTR’s standard rates:
1. disengagement planning services;
2. support for the migration of Your Content; and
3. other services as agreed as part of any disengagement plan.
5. Order Forms may be entered into with a reseller rather than directly with us. If that is the case your obligation to pay the Fees will be to the Reseller rather than to MATTR. All other obligations of this Agreement will apply directly between you and MATTR.
3) #### Term. [#term]
1. **Term for Services.** We will provide the Services and Materials to you for the Term specified in the Order Form. At the end of an initial fixed term, the Order Form will automatically renew for a further period of 12 months, unless either party gives notice that they wish the Order Form to terminate at least 30 days prior to its scheduled Order Form Expiry Date.
2. **Term of Customer Agreement.** This Customer Agreement will start on the Effective Date and will continue until all Order Forms and this Customer Agreement are terminated. If all Order Forms have expired or terminated, either party may terminate this Customer Agreement without cause on 30 days’ notice in writing to the other party.
4) #### Supply of Services. [#supply-of-services]
1. **General Supply commitments.** We will supply the Services to you:
1. in accordance with (i) the applicable Order Form and Service Terms; and (ii) this Customer Agreement;
2. in accordance with Good Industry Practice;
3. where no specific timeframe is provided in this Agreement, within a reasonable time;
4. with reasonable care and skill, using suitably qualified Personnel;
5. in accordance with Laws that apply to our business as a provider of IT products and services; and
6. in accordance with any Service quality requirements expressly specified in this Agreement.
2. **Open Source Components.** We may use Open Source Components in the Services and/or Materials, provided that we will ensure we do not incorporate any Open Source Components subject to a Copyleft Licence in Materials provided to you under this Agreement.
3. **Third Party Credential Capability Providers.** Use of the Services and/or Materials in connection with data, inputs or applications from a Third Party Credential Capability Provider is not included in an Order Form except to the extent expressly stated. If we do permit use with a Third Party Credential Capability Provider, it may be subject to: (i) application and approval by the Third Party Credential Capability Provider of you and each of your customers, (ii) additional Fees or charges; and (iii) you and/or your customers agreeing to certain additional terms and conditions with us and/or the Third Party Credential Capability Provider. You are solely responsible for providing full and accurate details of your and/or your customer’s business to support any application to the Third Party Credential Capability Provider, paying any additional Fees or charges and for accepting (and procuring that your customers accept) any additional terms and conditions. We are not responsible for the contents of your application or the application of any of your customers, any refusal of the Third Party Credential Capability Provider to permit your proposed use, or for ensuring that you and your customers comply with additional terms and conditions. Any services, materials, data or inputs provided by a Third Party Credential Capability Provider will be provided subject to the additional terms and conditions agreed by you, provided that our liability to you in respect of the Third Party Credential Capability Provider remains subject to the limitations of liability in this Agreement.
4. **Use of Agents.** Subject to clause 9.4, you may appoint agents or IT service providers (Agents) to exercise your rights and act on your behalf in respect of the Services and/or Materials provided under this Agreement. If you choose to do this, you will be responsible to us for ensuring Agents comply with this Agreement at all times and any acts or omissions of such Agents.
5) #### Changes. [#changes]
1. **Variations to this Agreement.** Except as set out below, any variations to this Agreement will be agreed in writing by the parties.
2. **Fees.**
1. For Fees calculated by reference to hourly or daily rates of our Personnel, we may change such hourly or daily rates on each anniversary of the signing of an Order Form.
2. For fixed Fees or subscription charges, we may propose an increase to such Fees to take effect after the expiry of the initial fixed term of any Order Form (i.e. that is, prior to you exercising any renewal under that Order Form).
3. **Support Terms and Data Processing Terms.** We may make changes or updates to the Support Terms or the Data Processing Terms from time to time, provided that subject to clause 5.6 below, these changes will not take effect until the expiry of the initial fixed term of any Order Form (i.e. that is, the changes will apply only if you exercise any renewal of Order Form).
4. **Service Changes.** We may make changes to the Services and/or Materials provided to you under an Order Form from time to time, provided that we ensure:
1. the applicable Services and/or Materials continue to substantially comply with the applicable Order Form and Service Terms; and
2. we do not materially diminish the core functionality of the applicable Service and/or Materials.
5. **Retired and End of Life.** We will update and replace functionality in respect of the Services and/or Materials (including our APIs) in accordance with the process set out in the Support Terms, which specifies how we will retire and make end of life former versions and how we handle breaking changes.
6. **Critical changes.** Despite any other provision of this Agreement, we may make changes to the Services and/or Materials, Data Processing Terms or any Support Terms without notice to:
1. address or avoid a security or Intellectual Property risk to us or the Services or Materials;
2. avoid any violation of any Law or reflect any regulatory change; or
3. maintain the commercial viability, security and availability of the Services or Materials.
6) #### Data security and storage. [#data-security-and-storage]
1. **Data security.** Without limiting (i) clause 17; (ii) your obligations under clause 9; or (iii) any specific security obligations set out in an Order Form, we will maintain an information security programme (including the adoption and enforcement of internal policies and procedures) designed to:
1. protect the Services, Materials and Your Content against accidental or unlawful loss, access, modification or disclosure;
2. identify reasonably foreseeable and internal risks to security and unauthorised access; and
3. minimise security risks, including through regular risk assessments and testing.
2. **Data region.** An Order Form may specify a Region in respect of Your Content. Where a Region is specified in an Order Form, you agree that we will process Your Content in that Region and any other locations set out in an Order Form, provided that:
1. certain types of data may be provided to our data sub-processors pursuant to clause 6.3 below, at the location for each sub-processor specified at [https://learn.mattr.global/docs/resources/terms/data-sub-processors](https://learn.mattr.global/docs/resources/terms/data-sub-processors) from time to time; and
2. where necessary in connection with providing the Services or administering this Agreement, our personnel located in another location (for example, New Zealand) may access Your Content as stored within the Region or where the systems of our data sub-processors are located.
3. **Data sub-processors.** In connection with providing the Services and/or Materials, we may provide Your Content to the data sub-processors set out at: [https://learn.mattr.global/docs/resources/terms/data-sub-processors](https://learn.mattr.global/docs/resources/terms/data-sub-processors) for the purposes specified for each data sub-processor. We will ensure we have appropriate contract terms in place with each data sub processor that require them to process Your Content on our instructions. We may from time to time change our data sub-processors or add new data sub-processors. Except where required to resolve an urgent security incident or to urgently ensure the continued availability of the Services and/or Materials, we will provide you 30 days’ notice of any changes to our data sub-processors who process personal information. If you notify us within 15 days of our notice of any concerns about the change in data sub-processor we will discuss those concerns with you and work in good faith with you to explore any solutions.
4. **Data minimisation.** In respect of any transfer of Your Content outside a Region, we will adhere to data minimisation principles, transferring only the minimum amount of Your Content needed for the applicable purpose, with any Personal Information first minimised, removed or anonymised where practicable.
5. **Customer Representative Information.** To provide billing, administration and support services, we may use and process your Customer Representative Information in the Region that applies to Your Content and in other regions where our billing, administration and support services are located.
6. **Data access.** We will not access, use or disclose Your Content or Customer Representative Information except as is necessary or required to:
1. maintain or provide the Services and Materials;
2. comply with the Law or legal processes, or to exercise, establish or defend our legal rights (and if we are allowed to, we will notify you of any request to disclose); or
3. mitigate potential negative implications of a disaster or security incident (as determined by us) which may include transfer of your data to a different location within the Region if one is available.
7. **Derived Data and Anonymised Statistical Data.** When you use our Services and/or Materials, we may generate operational and statistical data (such as performance metrics, usage statistics, success and failure rates, risk signals, and operational, security and compliance analytics) based on your use of the Services and/or Materials and the data processed through them, including through aggregation (“Derived Data”). We will use Derived Data to support delivery of the Services and/or Materials to you and other operational purposes in connection with such delivery, including monitoring acceptable use, security, compliance and risk monitoring. To the extent the Derived Data has been anonymised such that it does not identify you or any individuals, we may also use it to improve our Services and/or Materials and develop new services or product offerings, identify business trends or provide anonymous reporting of overall MATTR system performance, volume and uptime.
7) #### Data use and privacy. [#data-use-and-privacy]
1. **Customer Representatives.** We respect privacy and take data protection seriously. We will only collect, use, disclose and handle Customer Representative Information which is personal information in accordance with our Privacy Policy, which describes in more detail how we handle this information.
2. **End Users.** The way you choose to configure and use our Services and/or Materials is your responsibility. Your use of our Services and/or Materials may result in you processing the personal information of End Users. You must ensure you comply with all applicable data protection and privacy laws when using our Services and Materials, including by:
1. making available to each category of End User all required information about how their personal information will be collected, used, stored, transferred, processed and/or disclosed as required under applicable data protection and privacy laws; and
2. obtaining any required End User consents for the collection, use, storage, transfer, processing and disclosure of personal information, sensitive information or special category data (as those terms are defined in applicable data protection and privacy laws).
3. **Compliance to specific laws.** We will expressly agree with you in an Order Form if you or any of your End Users may provide to us: (i) Personal Information subject to the laws of China, Brazil, the EU (including GDPR), UK or Switzerland; (ii) “protected health information” pursuant to HIPAA; or (iii) credit card information or other data which requires PCI DSS compliance. Any such Order Form will detail any additional compliance steps to be taken in order to provide the Services and/or Materials pursuant to such laws or standards. Except to the extent agreed in an Order Form, you will not provide any such information to us for processing in connection with this Agreement.
4. **Data breach notifications.** If we think there has been unauthorised access to, or disclosure of, personal information inside your Account, we will let you know and endeavour to give you information about what happened. Depending on the nature of the unauthorised access or disclosure, and the location of those affected, you may be required to assess whether the unauthorised access or disclosure must be reported to those affected and/or a relevant authority. We will rely on you to make this decision, because you will have the most knowledge about any personal information that may be affected.
8) #### Confidentiality. [#confidentiality]
1. **Confidentiality.** Each party must keep confidential all Confidential Information of the other party, except to the extent that:
1. the recipient needs to disclose the Confidential Information to its professional advisors, provided that the recipient’s professional advisors are contractually required themselves to keep the Confidential Information confidential;
2. disclosure is required by Law or a Stock Exchange requirement, in which case, the recipient of the Confidential Information must promptly, where possible and permitted by Law, notify the discloser and only disclose the portion of Confidential Information necessary to satisfy the requirement; or
3. the recipient needs to disclose the Confidential Information to its Personnel to perform its obligations and exercise its rights under this Agreement.
2. **Protection of Confidential Information.** Without limiting clause 8.1, each party must take reasonable steps in accordance with Good Industry Practice to protect the confidentiality of the other party’s Confidential Information.
3. **Return or destruction of Confidential Information.** Subject to clause 8.4, following expiration or termination of this Agreement, or upon request, each party will return or destroy the other party’s Confidential Information within a reasonable period.
4. **Retention of Confidential Information.** Despite clause 8.3, each party may retain copies of Confidential Information for its record keeping purposes, for any purposes relating to the performance or enforcement of this Agreement or that party’s rights under it, as required by Law, that are preserved by the usual operation of the party’s information systems and processes or that also relate to continuing supply of any Services, provided that clause 8.1 will continue to apply to that Confidential Information.
5. **Publicity.** Subject to clause 8.1(b) and without limiting clause 8.1(b) or 8.6, neither party will make public announcements or statements in relation to this Agreement without the other party’s prior consent (which neither party will unreasonably withhold).
6. **Reference to Customer.** We may acknowledge that you are a MATTR customer and use Your Marks (in accordance with any reasonable guidelines you have provided to us) in any press release, marketing, sales, proposal or Stock Exchange reporting materials, including by featuring your name and/or logo on our website.
9) #### Your responsibilities. [#your-responsibilities]
1. **General responsibilities.** You must:
1. ensure that Your Content, and its use (including by End Users) comply with this Agreement and all applicable Laws;
2. use and configure the Services, Materials and any integration, portals, mobile applications or user interfaces that you create that interact with the Services and/or Materials in accordance with the Documentation;
3. ensure that Content provided to us in connection with this Agreement is accurate, complete, up-to-date and not misleading in any material respect;
4. provide information, co-operation and assistance to us and our Personnel on reasonable request;
5. make suitable Personnel available to us, ensure that they attend meetings, answer questions, provide information, and make decisions in a timely fashion, and ensure that any information they provide to us is accurate, up-to-date and complete;
6. comply with our reasonable directions in connection with the security and integrity of the Services;
7. properly configure and use the Services, including any Materials provided as part of the Services;
8. not provide Content to us to the extent it is subject to additional security measures not agreed in writing;
9. only use Services and Materials within the restrictions and limitations specified in the applicable Service Terms; and
10. comply with any of your other responsibilities as set out in an Order Form.
2. **Account and payment.** To access the Services, you must have an Account associated with a valid email address and a valid form of payment or agreed payment process in a signed Order Form.
3. **Trial Access.** We may provide you with Trial Access to Services and/or Materials. To the extent you are receiving Trial Access, either party may terminate that Trial Access for convenience at any time. Any Services and/or Materials provided via Trial Access are provided solely on an “as is” and “as available” basis and clauses 4.1 and 17.1 do not apply. You agree that we may not meet the service levels specified in the Support Terms and you will not be eligible for Service Credits in respect of Trial Access. We may do any of the following in respect of Services and/or Materials provided via Trial Access: (i) vary the Services and/or Materials at any time (including by making breaking changes, or changes that materially diminish the functionality you receive) without notice to you; (ii) remove, replace, retire or make end of life any part of the Services and/or Materials without notice to you and without complying with the Support Terms, (iii) suspend your access to all or any part of the Services and/or Materials without cause; (iv) amend the terms of this Customer Agreement, the Support Terms and the Data Processing Terms immediately on notice in writing to you. The indemnity in clause 16.2 does not apply to any Services and/or Materials provided via Trial Access. You must not use Services and/or Materials provided via Trial Access in production, for any commercial use or with any of your Confidential Information or personal information of real individuals without our express prior consent. The provisions of this clause 9.3 apply in respect of Trial Access notwithstanding any other provisions of this Agreement.
4. **Services requiring access credentials.** If a Service involves the use of access credentials (such as passwords, private keys, API keys, 3rd party secrets for access to claim sources or other secrets) by you or your Customer Representatives or End Users, you are responsible for protecting such credentials against unauthorised use or disclosure and you must not sell, share, transfer or sub-licence them except:
1. to the extent that we permit in writing (which we may permit subject to conditions); and
2. that you may disclose credentials to your Personnel performing work on your behalf.
5. **Your accounts.** Except to the extent caused by our breach of this Agreement:
1. you are responsible for all activities that occur under your Account, regardless of whether the activities are authorised by you or undertaken by you, your Personnel, Agents, Customer Representatives, End Users or a third party; and
2. we and our Affiliates and Personnel are not responsible for unauthorised access, interference or modification to your Account, or any unauthorised access, modification, loss or disclosure of Your Content or personal information or other data in your Account.
6. **End Users.** You are responsible for Customer Representatives’ and End Users’ use of Your Content, the Services and the Materials. You must ensure all End Users comply with the terms of this Agreement and agree to terms that are consistent with this Agreement. We do not have to provide any support or services to End Users unless we have a separate agreement with you or them for the provision of such support or services or are required to support them by Law (e.g. with access to personal information).
7. **Sandbox and prototypes.** If we make available a sandbox environment, or any demo, tech preview, beta or prototype Services and/or Materials, you may only use such Services and/or Materials with dummy or test data unless we provide express written consent for wider use. You must only use such Services and/or Materials for the sole purpose of evaluating capabilities in a non-production, non-commercial environment. You will ensure that all data entered into such Services and/or Materials by you and your Customer Representatives is not data that is, or is likely to constitute, personal information or Confidential Information, unless you have our express prior written consent for such use.
8. **Failure to meet your obligations.** We will not be responsible for any failure to meet or delay meeting our obligations under this Agreement to the extent caused or contributed to by:
1. your failure to comply with your obligations under this Agreement or failure to provide timely input or decisions or take appropriate action; and/or
2. your information, material, input or decisions,
each an “Excused Event”.
You will still be responsible for paying the Fees applicable for a period affected by an Excused Event.
10) #### Unacceptable use. [#unacceptable-use]
1. **Overview.** You must not, and must ensure that Customer Representatives and End Users will not, use the Services or Materials in any manner or for any purpose other than as expressly permitted by this Agreement. The examples described in this clause are not exhaustive. If you do not comply, and ensure all Customer Representatives and End Users comply, with this clause, we may suspend or terminate your access to and use of the Services in accordance with clauses 13 or 14 as appropriate.
2. **Illegal, harmful, or offensive use or content.** You must not use, or encourage, promote, facilitate or instruct others to use, the Services or Materials in a manner that is illegal, harmful, fraudulent, offensive or that infringes on the right of any person, or to transmit, store, display, distribute or otherwise make available Content that we consider to be illegal, harmful, fraudulent, or offensive.
3. **Malware.** You must not use the Services or Materials to host or distribute any Content or other computer technology that may damage, interfere with, surreptitiously intercept, or expropriate any communication, network, system, software application, network or computing device or data (including viruses, Trojan horses, worms, time bombs, cancelbots and other malware).
4. **Unauthorised access.** You must not access or use the Services or Materials to threaten, attempt to, or engage in conduct that would violate the security or integrity of (including through a malicious act), or otherwise gain unauthorised access to, any communication, network, system, software application, network or computing device or data.
5. **Network interference.** You must not threaten, attempt, or engage in, conduct that is likely to interfere with, pose a security risk to, or adversely impact our systems, Services, Materials or Site, including the Content of our customers and their use of our Services, Materials or Site. For clarity, this includes making network connections to any system or network (unless you have permission to do so), monitoring or crawling systems or networks in a way that impairs or disrupts them, denial of service attacks, intentionally interfering with any system or network, or using any means to avoid usage limitations and restrictions placed on the Services, Materials, Site or any other system or network.
6. **License restrictions.** You must not breach the licence restrictions set out in clause 15.6.
7. **Monitoring and enforcement.** We may, but are not obliged to, investigate any violation of this clause or any other misuse of the Services or Materials. We may report any activity that we suspect violates any Law to law enforcement officials, regulators, or other appropriate third parties. Our reporting may include disclosing Customer Representative Information. We also may cooperate with law enforcement agencies, regulators, or other appropriate third parties to help with the investigation and prosecution of suspected or actual illegal conduct by providing network and systems information related to alleged violations of this Agreement or the Law.
11) #### Fees and payment. [#fees-and-payment]
1. **Fees.**
1. In exchange for our commitment to supply Services and/or Materials, you must pay us the Fees specified in an Order Form.
2. To the extent that an Order Form specifies any:
1. fixed Fee, we will charge you that fixed Fee;
2. variable Fee (other than an estimated time and materials Fee), we will charge in accordance with the specified calculation methodology for the variable Fee;
3. estimated or time and materials Fee, we will charge time and materials Fees at the rates specified (or our standard rates, if no such rates are specified); or
4. Fee in respect of a defined quantity or allocation of Services or Materials (e.g. a number of End Users, or a fixed number of hours of professional services), the full Fee set out in the Order Form will be charged irrespective of whether you use the entire quantity or allocation.
3. Any estimate of time and materials Fees must be calculated by MATTR in good faith based on the information available to it at the time, but are estimates only.
4. Unless otherwise expressly stated in an Order Form, payment of Fees is not conditional on acceptance or use of Services or Materials to be provided as part of the Services.
5. Unless expressly stated otherwise in an Order Form, any reference to a monetary amount is a reference to United States Dollars.
2. **Invoicing.** We will issue invoices to you in respect of the Fees as specified in the Order Form. Invoices are payable by the date specified in the Order Form, or if no date is specified, by the 20th of the month following the month in which the invoice is received.
3. **When charging starts.** We may begin charging you for a Service from the earliest of:
1. the date on which we first make any part of the Service or Materials available to you; or
2. the date specified in an Order Form.
Unless we cause a delay in making Services or Materials available, charging will apply in accordance with this clause even if you do not start consuming the relevant Service or Materials at the time charging is permitted to start.
4. **Costs and disbursements.** In addition to the Fees, you will pay us for all pre-approved costs and expenses incurred by us in connection with providing you the Services and Materials.
5. **Payment terms.** You must pay all Fees payable under this Agreement by the due date without any set-off, counterclaim, deduction or other withholding. We may charge you interest at the Overdue Interest Rate on any unpaid Fees from the date such unpaid Fees became due until the date payment is received into our account in cleared funds. However, if the Overdue Interest Rate exceeds the maximum permitted legal interest rate, the interest chargeable will be reduced to reflect the maximum permitted legal interest rate.
12) #### Taxes. [#taxes]
1. **Fees exclusive of Taxes.** Our Fees exclude Sales Taxes and all other Taxes. If we are liable for any Sales Taxes or any other Tax in respect of the provision of the Services or Materials or any other supply we make under this Agreement, you will pay us, in addition to and at the same time as our Fees, an amount equal to the amount of such Tax, subject to receipt of a tax invoice (if applicable).
2. **Withholding Tax.** If:
1. you are required by Law to make any deduction or withholding from any amount payable to us under this Agreement; or
2. we are required by Law to pay any Tax in relation to any amount receivable by us under this Agreement,
you must pay to us such additional amounts as are necessary so that, after making the deduction, withholding or payment, the net amount received and retained by us is equal to the amount we would have received and retained had no such deduction, withholding or payment been made.
13) #### Suspension. [#suspension]
1. **General.** We may suspend your right to access or use any or all Services and Materials immediately upon notice to you to the extent that:
1. we determine on reasonable grounds that your or any End User’s use of the Services or Materials: (i) is in breach of clauses 7, 9, or 10; (ii) could subject us, our Affiliates, our Personnel or any third party, to liability; (iii) could be fraudulent; or (iv) is prohibited under clause 21.6;
2. you are otherwise in breach of this Agreement;
3. you have failed to pay any required amount under clause 11 in respect of any Service or Materials by the due date; or
4. you have ceased to operate in the ordinary course of business, made an assignment for the benefit of creditors or similar disposition of your assets, or become the subject of any insolvency, bankruptcy, reorganisation, liquidation, dissolution or similar proceeding.
2. **Effect of suspension.** If we suspend your right to access or use any or all Services or Materials:
1. you remain responsible for all Fees you incur during the period of suspension; and
2. you will not be entitled to any Service Credits under the Support Terms for any period of suspension.
14) #### Termination. [#termination]
1. **Termination for cause.** Either party may terminate this Agreement if the other party is in material breach of this Agreement and the breach remains uncured for a period of 30 days from receipt of written notice by the other party.
2. **Immediate termination by us.** We may also terminate this Agreement, any one or more Order Forms or any one or more Services under an Order Form immediately upon notice to you:
1. to the extent we have the right to suspend under clause 13;
2. if our relationship with a third-party partner who provides software or other technology we use to provide affected Services or Materials expires, terminates or requires us to change the way we provide the software or other technology as part of the Services or Materials;
3. to comply with any applicable Law or legal processes; or
4. if we are subject to any legal or regulatory changes that we consider make it technically or commercially burdensome for us to continue providing the affected Services or Materials to you.
3. **Effect of termination.** Upon the Termination Date of this Agreement or any Order Form:
1. except as provided in clause 14.4, all your rights under this Agreement or the applicable Order Form with respect to affected Services and Materials immediately terminate;
2. if one or more Services or Order Forms is terminated (but not other Services, Order Forms or the entire Agreement), such termination will not affect any remaining Services, Materials or Order Forms in effect between the parties or this Customer Agreement;
3. you remain responsible for all Fees you have incurred for affected Services and Materials up until, and including, the Termination Date and are responsible for any Fees you incur for those affected Services or Materials during the post-termination period described in clause 14.4;
4. you will immediately return or, if instructed by us, destroy all Material provided in connection with affected Services which are in your possession; and
5. clauses 1, 9.5(a), 11, 14, 15 (except the licence granted to you in clause 15.5), 16, 17, 18 and 21 will continue to apply in connection with the affected Services and Materials.
4. **Consequences of termination.** Unless we terminate this Agreement or any Order Form pursuant to clause 14.1:
1. we will not take action to remove Your Content from the Services as a result of the termination for 30 days following the Termination Date; and
2. we may remove Your Content from the Services any time following that 30 day period unless otherwise agreed with you in writing.
5. **Use after Termination Date.** For any use of the Services or Materials after the Termination Date, your obligations under this Agreement will apply, and you will pay the applicable Fees in accordance with clause 11.
15) #### Intellectual Property. [#intellectual-property]
1. **Your Content.** You own Your Content. When you transfer Your Content into our Services, you grant us a non-exclusive, transferable and sublicensable licence to use, copy, communicate, transmit, store, analyse, adapt and back up all transferred data to provide the Services and Materials to you and End Users.
2. **Adequate rights.** You represent and warrant to us that:
1. you or your licensors own all right, title, and interest in and to Your Content and Feedback, including to Intellectual Property;
2. you have all rights in Your Content and Feedback necessary to grant the rights contemplated by this Agreement; and
3. none of Your Content and End Users’ use of Your Content or the Services, Materials or Site will breach clause 7, 9, or 10, or otherwise breach any applicable third-party licence, developer or other terms that apply (e.g. Google Pay or Apple Wallet terms).
3. **MATTR IP.** Despite any other provision of this Agreement, we or our licensors own all rights, title, and interest (including Intellectual Property) in and to:
1. the Services, Site, the MATTR Marks and Materials; and
2. any changes, updates or improvements to the Services, Site, MATTR Marks and Materials over the course of this Agreement, including (i) where made by us in the course of providing Services to you or (ii) where we have outlined certain changes we intend to make with you, discussed changes with you, and/or asked for your Feedback.
4. **Newly developed IP.** Except to the extent expressly set out in any Service Terms, if any professional services we provide to you result in the creation of any Intellectual Property, such Intellectual Property will vest in us immediately on its creation and will be licensed to you pursuant to clause 15.5.
5. **Your licence to use our Services.** Subject to the terms of this Agreement, we grant you a limited, revocable, non-exclusive, non-transferrable licence for the duration of this Agreement to access and use the Services and Materials solely in accordance with this Agreement, including subject to clauses 9 and 10. Except as provided in this clause 15.5, you obtain no rights under this Agreement from us, our Affiliates or our licensors to the Services, Site, Materials or the MATTR Marks, including any Intellectual Property.
6. **Licence restrictions.** You must not, and must not permit End Users to, access or use the Services or Materials in any manner or for any purpose other than as expressly permitted by this Agreement. You must not and must ensure End Users do not, or do not attempt to:
1. modify, distribute, alter, tamper with, repair, or otherwise create derivative works of any Content included in the Services or Materials (except to the extent Content is provided to you under a separate licence that expressly permits the creation of derivative works);
2. reverse engineer, disassemble, or decompile the Services or Materials or apply any other process or procedure to derive the source code of any software included in the Services or Materials (except to the extent applicable Law doesn’t allow this restriction);
3. access or use the Services or Materials in a way intended to avoid incurring fees, bypass usage limits or bypass quotas.
7. **Onward distribution.** You must not resell, sublicense or otherwise make available to third parties the Services or Materials except to the extent expressly permitted under a separate licence or in an Order Form or applicable Service Terms. This will not restrict you from using Agents to act on your behalf in respect of the Services and Materials pursuant to clause 4.4.
8. **MATTR Marks.** You may only use the MATTR Marks with our prior written permission and in accordance with any trademark usage guidelines that we have published on the Site or otherwise notified to you.
9. **Feedback.** You are not obliged to provide Feedback to us or our Affiliates. If you do so:
1. we and our Affiliates may use, disclose, modify or develop the Feedback for any purpose without restriction and without attribution or compensation to you or any other person; and
2. you irrevocably assign to us all right, title, and interest in and to the Feedback and agree to provide us any assistance we require to document, perfect, and maintain our rights in the Feedback.
10. **Know-how.** Without limiting our obligation to maintain the confidentiality of your Confidential Information in accordance with clause 8, our Personnel will retain and may re-use techniques, ideas, concepts, information or know-how relating to methods or processes of general application obtained in the course of providing the Services and Materials to you.
16) #### Indemnity. [#indemnity]
1. **General.** You will defend, indemnify, and hold harmless us, our Affiliates and our licensors, service providers and suppliers, (and each of our and their respective Personnel) from and against any third-party claim against us arising out of or in connection with:
1. your or any Customer Representatives’ or End Users’ access to or use of the Services or Materials (including any activities under your Account and use by your Personnel); or
2. a dispute between you and any End User.
You will reimburse us for reasonable legal fees, as well as time and materials spent by our Personnel responding to any third party subpoena or other compulsory legal order or process associated with third party claims described in this clause at our then-current hourly rates.
2. **The Services.** Subject to the limitations in this clause 16, we will defend you and your employees, officers, and directors against any third-party claim alleging that the Services infringe or misappropriate that third party’s Intellectual Property, and will pay the amount of any adverse final judgment or any settlement agreed by us.
3. **Your Content.** Subject to the limitations in this clause 16, you will defend us, our Affiliates, and our and their respective Personnel against any third-party claim alleging that any of Your Content infringes or misappropriates that third party’s Intellectual Property, and will pay the amount of any adverse final judgment or settlement agreed by you.
4. **Exclusions.** We will have no obligations or liability to you under clause 16.2 arising out of or in connection with:
1. modification of the Services or Materials by anyone other than us or our Affiliates;
2. combination, operation or use of the Services or Materials with any other goods, software, product, data or services not provided by us; or
3. you, or any Customer Representative’s or End User’s use of the Services or Materials after we have notified you to discontinue such use.
The remedies provided in this clause 16 are the sole and exclusive remedies for any third-party claims of infringement or misappropriation of Intellectual Property by the Services or by Your Content.
5. **Remediation.** For any claim covered by clause 16.2 we will, at our option, either:
1. procure the rights to use that portion of the Services alleged to be infringing;
2. replace the alleged infringing portion of the Services with a non-infringing alternative;
3. modify the alleged infringing portion of the Services to make it non-infringing; or
4. terminate the allegedly infringing portion of the Services or this Agreement or both (as applicable).
6. **Process.** The party seeking defence or indemnity under this clause 16 must:
1. give the other party prompt written notice of the claim;
2. not make any admission and must not purport to settle the claim without the other party’s prior written consent;
3. permit the other party to control the defence and settlement of the claim; and
4. reasonably cooperate with the other party (at the other party’s expense) in the defence and settlement of the claim.
17) #### Warranties. [#warranties]
1. **Specification.** We warrant that each Service will substantially comply with the Specification for it set out in the applicable Service Terms.
2. **Exclusion of warranties.** To the maximum extent permitted by law, no warranties apply to the Services and/or Materials other than those expressly set out in this Agreement. Except to the extent prohibited by Law (or to the extent any statutory rights apply that cannot be excluded, limited or waived):
1. we and our Affiliates and licensors exclude all implied or statutory warranties, including any warranties arising out of any course of dealing or usage of trade regarding the Services and/or Materials; and
2. the Services and/or Materials are not provided subject to any warranties: (i) of merchantability, satisfactory quality, fitness for a particular purpose, non-infringement, or quiet enjoyment; (ii) that the Services and/or Materials will be uninterrupted, error free or free of harmful components; or (iii) that any Content will be secure or not otherwise lost or altered.
However, see clause 18.7.
18) #### Liability. [#liability]
1. **Limitation.** Subject to clauses 18.2-18.4, in no case will each party and its Affiliates’ and licensors’ aggregate liability under this Agreement to the other party exceed the amount you actually pay us under this Agreement for the Service or Materials that gave rise to the claim during the 12 months before the liability arose.
2. **Matters outside liability cap.** The limitation of liability in clause 18.1 does not apply to liability arising out of or in connection with any of the following:
1. fraud or wilful misconduct by either party or their Personnel;
2. your obligation to pay the Fees, interest on Fees, costs for the recovery of Fees, Taxes, gross-ups or any other amount due to us under clauses, 11 or 12;
3. our obligations under clause 16.2 and your obligations under clause 16.3;
4. infringement or unauthorised use of our Intellectual Property;
5. any indemnity by you under this Customer Agreement; and
6. either party’s liability for breach of clauses 7 or 8, provided that our total aggregate liability to you for breach of clauses 7 or 8 will instead be limited to 3 times the total Fees payable in the 12 months prior to the first event giving rise to liability.
3. **Exclusion.** Neither party nor its Affiliates will be liable to the other party under or in connection with this Agreement for (i) any indirect or consequential loss; or (ii) loss of profits, revenues, customers, opportunities, goodwill, use, or data or for any exemplary damages. In addition, we will not be liable to you for any compensation, reimbursement or damages arising out of or in connection with your inability to use the Services or Materials as a result of: (iii) this Agreement or your use of or access to the Services or Materials being terminated or suspended in accordance with this Agreement; or (iv) downtime or unavailability of any part of the Services or Materials, except as expressly stated in our Support Terms.
However, see clause 18.7.
4. **Recovery of data.** Our liability for loss of or damage to Your Content stored on our platform is limited to taking reasonable steps to recover the affected data from our available backups within the applicable Region.
5. **Application of Law.** The exclusions and limitations set out in this clause 18 apply only to the maximum extent permitted by applicable Law. See clause 18.7.
6. **Proportionate liability.** Each party’s liability for any Losses suffered by the other party under or in connection with this Agreement will be reduced proportionally to the extent the other party contributed to such Losses.
7. **Consumer Laws.** In some places, like New Zealand and Australia, there may be non-excludable warranties, guarantees or other rights provided by Law (Non-excludable Consumer Guarantees). They still apply – these terms do not exclude, restrict or modify them. Except for such Non-excludable Consumer Guarantees and other rights you have that we cannot exclude, we expressly exclude all warranties and guarantees and we are only bound by the express terms set out in this Agreement. Our liability for breach of a Non-excludable Consumer Guarantee is limited, at our option (and subject to clause 18.1), to either re-performing, refunding, replacing or paying the cost of replacing the relevant service (unless the Non-excludable Consumer Guarantee says otherwise, in which case it is limited to the greatest extent allowed by the Non-excludable Consumer Guarantee).
8. **Mitigation.** Each party must take reasonable steps to mitigate any Losses that it may suffer under or in connection with this Agreement.
19) #### Disputes. [#disputes]
1. **Addressing your concerns.** Most concerns can be resolved quickly and to everyone’s satisfaction by contacting our support team. If we are unable to resolve your complaint to your satisfaction (or if we haven’t been able to resolve a dispute we have with you after attempting to do so informally), you and we agree to resolve those disputes through binding arbitration in accordance with clause 19.2.
2. **Arbitration.** Any dispute or claim arising out of or in connection with this Agreement or the Services will be resolved by binding arbitration using a sole arbitrator administered by the Singapore International Arbitration Centre in accordance with the Arbitration Rules of the Singapore International Arbitration Centre. The arbitration will be conducted in Auckland, New Zealand using the English language in accordance with clause 21.5.
3. **Urgent relief.** Nothing in this Agreement will prevent either party seeking or obtaining any order or relief by way of injunction or declaration or other equitable or statutory remedy against the other party where that party believes such order or relief is necessary for the urgent protection of its rights or property, including Intellectual Property.
4. **Conduct of claims and litigation.** We agree with you that any dispute resolution proceedings will be conducted only on an individual basis and not in a class, consolidated or representative action. If for any reason a claim proceeds in court rather than in arbitration, we and you waive any right to a jury trial.
20) #### Notices. [#notices]
1. **Language.** All communications and notices made or given pursuant to this Agreement must be in the English language. If we provide a translation of the English language version of this Agreement, the English language version of the Agreement will control if there is any conflict.
2. **Giving notices.** We may provide notice to you under this Agreement by sending a message to the email address specified in an Order Form or otherwise associated with your Account. Any notice you send to us must be sent to [accounts@mattr.global](mailto:accounts@mattr.global). Notices will be effective upon receipt into the relevant inbox, provided that if received after 5 PM (NZST or NZDT, as applicable) on a Working Day, they will be deemed received at 9 AM (NZST or NZDT, as applicable) on the next Working Day.
21) #### General. [#general]
1. **No poaching.** For the duration of this Agreement and for 6 months after its termination, you must not directly or indirectly solicit or offer employment or a contract for services to any MATTR personnel, except with MATTR’s prior consent.
2. **Assignment.** You must not assign or otherwise transfer this Agreement or any of your rights and obligations under this Agreement, except with our prior written consent. Any assignment or transfer in violation of this clause 21 will be void. We may novate, assign or otherwise transfer this Agreement without your consent:
1. in connection with a merger, acquisition or sale of all or substantially all of our assets; or
2. to any Affiliate or as part of a corporate reorganisation,
and in the event of a novation or transfer, effective upon the novation or transfer, the new party nominated by us is deemed substituted for us as a party to this Agreement and we are fully released from all of our obligations and duties to perform under this Agreement. Subject to the foregoing, this Agreement will be binding upon, and inure to the benefit of the parties and their respective permitted successors and assigns.
3. **Entire agreement.** This Agreement is the entire agreement between you and us regarding its subject matter. It supersedes all prior or contemporaneous representations, understandings, agreements, or communications between you and us, (whether written or verbal) regarding the subject matter of this Agreement. We will not be bound by any term, condition or other provision that is different from or in addition to the provisions of this Agreement (whether or not it would materially alter this Agreement, and whether or not they are prior to, contemporaneous with, or subsequent to this Agreement) including terms contained in your purchase orders, receipts, confirmations, RFx documentation and other standard terms.
4. **Force majeure.** We and our Affiliates will not be liable for any delay or failure to perform any obligation under this Agreement where the delay or failure results from any cause beyond our reasonable control, including acts of God, labour disputes or other industrial disturbances, electrical or power outages, utilities or other telecommunications or cloud infrastructure failures, earthquake, storms or other elements of nature, epidemics, pandemics, blockages, embargoes, riots, acts or orders of government, acts of terrorism, or war.
5. **Governing Law and jurisdiction.** This Agreement will be governed by the Laws of New Zealand and subject to clause 19.2, you consent to the non-exclusive jurisdiction of the New Zealand courts. You must not object to the transfer of any proceedings to New Zealand courts on any basis, including inconvenience. The parties agree that United Nations Convention on Contracts for the International Sale of Goods does not apply to this Agreement.
6. **Export control.** Each party will comply with export control and sanctions laws and regulations applicable to its business. You are solely responsible for your compliance with applicable laws in relation to how you choose to use the Services, including the transfer and processing of Your Content, the provision of Your Content to End Users, and the Region in which any of the foregoing occur. You must not use our Services in violation of any export or trade embargo laws that apply to you. You represent and warrant that you are not in a jurisdiction subject to sanctions that would affect our provision of the Services or Material or otherwise designated on any list of prohibited or restricted parties maintained by the United Nations Security Council, the European Union or its Member States, the United States, New Zealand or other applicable government authority.
7. **Our relationship with you.** We and you are independent contractors, and this Agreement will not be construed to create a partnership, joint venture, agency, or employment relationship. Except as expressly provided in this Agreement, neither party, nor any of their respective Affiliates, or personnel is an agent of the other for any purpose or has the authority to bind the other.
8. **Third-Party beneficiaries.** This clause 21.8, clause 21.9 and the terms set out in clause 16 relating to indemnified third parties and the references in this Agreement to our Affiliates, licensors, service providers and suppliers are expressed for the benefit of those persons for the purposes of the Contract and Commercial Law Act 2017 (Part 2, Subpart 1). Otherwise, this Agreement does not create any third-party beneficiary rights in any individual or entity that is not a party to this Agreement.
9. **You may only make claims against us.** Where you have any claim arising out of or in connection with the Services, Materials, this Agreement, you may only make that claim directly against us (and not against any of our Personnel, Affiliates, licensors, service providers and suppliers). None of our Affiliates, licensors, suppliers and service providers (or their Personnel), will be liable to you or required to compensate you for any Losses of any kind that you may suffer in connection with the Services, Materials or this Agreement.
10. **Material Adverse Change.** For the purposes of this clause, a “Material Adverse Change” will mean any state or federal government action, decision, executive order, change in Laws, regulatory change (to avoid doubt including the introduction of any new taxes, import or export duties, tariffs or export controls) occurring during the Term which has or is likely to have a material adverse impact on: (i) our compliance with this Agreement; or (ii) the technical, operational or commercial viability of our provision of the Services or Materials as envisaged by this Agreement. Despite any other provision of this Agreement, if a Material Adverse Change occurs during the Term:
1. we may give notice to you setting out details of the Material Adverse Change and any proposed variation to this Agreement (including to the Fees, Services or Materials);
2. the parties will promptly meet to discuss the Material Adverse Change and endeavour to agree any appropriate variation to this Agreement; and
3. if the parties cannot agree on a variation within 30 days of notice of a Material Adverse Change, we may terminate the applicable Order Form or the Agreement by 15 Working Days’ notice in writing.
11. **Non-exclusive.** Nothing in this Agreement implies an obligation of exclusivity on us. You acknowledge that we may supply the same or similar services or materials to other customers.
12. **No waiver.** The failure by us to enforce any provision of this Agreement will not constitute a present or future waiver of such provision nor limit our right to enforce such provision or any other provision at a later time. All waivers by us must be in writing to be effective.
13. **Severability.** If any portion of this Agreement is held to be invalid or unenforceable, the remaining portions of this Agreement will remain in full force and effect. Any invalid or unenforceable portions will be interpreted to give effect to the intent of the original portion. If such construction is not possible, the invalid or unenforceable portion will be severed from this Agreement, but the rest of the Agreement will remain in full force and effect.
## Previous versions [#previous-versions]
* [MATTR Customer Agreement - 30 June 2022 (archived)](/docs/resources/terms/customer-agreement/30-6-22)
* [MATTR Customer Agreement - 20 April 2022 (archived)](/docs/resources/terms/customer-agreement/20-4-22)
* [MATTR Customer Agreement - 25 March 2021 (archived)](/docs/resources/terms/customer-agreement/25-3-21)
# MATTR Customer Agreement - What's Changed
URL: /docs/resources/terms/customer-agreement/recent-changes
## Changes posted April 21, 2026 [#changes-posted-april-21-2026]
MATTR updated the [MATTR Customer Agreement](/docs/resources/terms/customer-agreement) on April 21, 2026.
This is a comprehensive update reflecting MATTR's evolution from a self-service platform model to an enterprise-focused, Order Form-based commercial relationship. The update reflects the inclusion of a wide range of additional protections for Customers that support its use for commercial orders and use in production. At a glance, here are the key changes:
1. **New agreement structure.** The agreement has been reorganised and expanded from 20 to 21
clauses. Definitions have moved to the front (clause 1), and a new "Agreement" clause (clause 2)
describes how Order Forms, Service Terms, Support Terms and Data Processing Terms fit together in
a clear order of priority.
2. **Order Form-based model.** The agreement now caters for individually negotiated Order Forms that
specify Services, Fees, Terms, Regions and other commercial details, replacing the previous
self-service, pay-as-you-go approach.
3. **Expanded definitions.** Many new defined terms have been introduced to support the broader scope
of services, including Agent, Affiliate, Customer Application User, Customer Representative,
Holder, Issuer, MATTR User, Verifier, Service Terms, Support Terms, Specification, and Third
Party Credential Capability Provider. The former "Account Information" concept has been replaced
by "Customer Representative Information" with a clearer scope.
4. **Supply commitments and warranties.** For commercial orders, MATTR now commits to a number of service warranties and commitments, including that each Service will substantially comply with its Specification (clause 17.1), and obligations to comply with Good Industry Practice, use reasonable care and skill, and comply with applicable Laws.
5. **Dedicated confidentiality section.** Confidentiality commitments by MATTR are now included in a standalone
clause (clause 8) with detailed provisions for protection, return or destruction, retention
rights, publicity and customer reference rights, replacing a single paragraph in the former
General section.
6. **Enhanced data provisions.** New provisions cover data sub-processors (with a 30-day change
notification process), data minimisation principles for cross-Region transfers, Derived Data and
anonymised statistical data usage, and more detailed data breach notification obligations.
7. **Trial Access.** The former brief trial period clause has been replaced with a detailed Trial
Access provision (clause 9.3) that clearly sets out reduced service commitments applicable to free trial access (e.g. when you sign up on our website).
8. **Liability and indemnity.** The liability framework has been expanded to reflect common customer requests in commercial negotiations, including a specific list of matters excluded from the liability cap (clause
18.2), and an enhanced liability for breach of confidentiality and data obligations via a new super cap of three times the Fees payable in the preceding 12 months.
9. **Arbitration.** Dispute resolution has moved from the New Zealand Arbitration Act 1996 to the
Singapore International Arbitration Centre (SIAC) rules, while retaining Auckland, New Zealand as
the seat of arbitration.
10. **New general provisions.** Several new clauses have been added, including a no-poaching
obligation (clause 21.1), a Material Adverse Change clause enabling renegotiation or termination
in response to significant regulatory or government changes (clause 21.10), a non-exclusivity
clause (clause 21.11), and a clause limiting claims to MATTR directly (clause 21.9).
11. **Terminology updates.** "Related companies" has been replaced with "Affiliates" throughout, and
"Personnel" now formally covers agents, officers, employees and contractors.
By continuing to use the Services or Materials 31 days after the effective date of these and any
changes to the MATTR Customer Agreement, you agree to be bound by the modified terms.
Previous versions are available for reference, and archival purposes.
## Changes posted 30 Jun 2022 [#changes-posted-30-jun-2022]
MATTR updated the [MATTR Customer Agreement](/docs/resources/terms/customer-agreement) on 30 Jun 2022.
We've updated our Customer Agreement so that it covers everything it needs to, because it's
important we're all on the same page.
The changes won’t affect the way you use MATTR VII or our SDK platform. They’ll make it easier for
you to understand what to expect from MATTR - and what is controlled by other parties. At a glance,
here are the key changes:
1. We've made it more obvious that access is required to your Google Pay / Apple Wallet merchant
credentials, to support the creation of compliant passes.
2. We've clarified that when you create Google Pay / Apple Wallet passes, you'll need to ensure they
align with Google and Apple’s respective licence/developer terms. If a pass created doesn't align
with those, it may mean that you are no longer upholding your end of the agreement we have
together.
By continuing to use the Services or Materials 31 days after the effective date of these and any
changes to the MATTR Customer Agreement, you agree to be bound by the modified terms.
Previous versions are available for reference, and archival purposes.
# MATTR Data Processing Terms
URL: /docs/resources/terms/data-processing-terms
Last Updated: 25 March 2021
[See what's changed](/docs/resources/terms/data-processing-terms/recent-changes) |
[Previous versions](#previous-versions)
These Data Processing Terms apply to the Services supplied by MATTR Limited if you or your End Users
access or use the Services in the European Economic Area or are otherwise subject to the GDPR or
legislation implementing the GDPR.
The Data Processing Terms set out terms that apply to the Processing of your Personal Data and
Personal Data of your End Users by MATTR as set out in clause 5.2 of the MATTR Customer Agreement.
In these Data Processing Terms:
* “**Data Subject”** means your End Users, employees, contractors and agents, who in each case are
natural persons to the extent that they are identified or identifiable and, if you are a natural
person, you;
* “**Data Controller**” means you (the party that has entered into the MATTR Customer Agreement);
* “**Data Processor**” means MATTR Limited; and
* other capitalised terms which are not defined in the Data Processing Terms have the meanings set
out in the MATTR Customer Agreement.
To provide the Services to the Data Controller, the Data Processor will store and Process Personal
Data of Data Subjects in accordance with these MATTR Data Processing Terms. Where there is any
conflict or ambiguity between these Data Processing Terms and the MATTR Customer Agreement, these
Data Processing Terms take priority.
1. #### Appointment of Data Processor [#appointment-of-data-processor]
1. **Appointment.** Data Controller appoints Data Processor to Process Personal Data in
accordance with clause 1.2.
2. **Processing Details.** Data Processor may Process the Personal Data of Data Subjects as
follows:
1. **Duration of Processing.** For the Duration of the Term in accordance with the MATTR
Customer Agreement, for any period that the Data Processor is permitted to continue
Processing to fulfil its obligations or exercise its rights under the MATTR Customer
Agreement (e.g. to allow the Data Controller access to Your Content after termination),
and for any longer period required by law (e.g. to maintain statutory records).
2. **Nature and Purpose of Processing.** To provide any Services or Material to the Data
Controller as contemplated by the MATTR Customer Agreement.
3. **Type of Personal Data.** The Data Subject's:
* name, date of birth, customer identifier, email address, phone number, physical or
postal address details, other contact details, bank account details and an IP address
(to the extent that the Data Subject is identifiable from it);
* communications to the Data Processor or with other End Users (e.g. in community
support forums); and
* any other personal data provided by Data Subjects or on their behalf to the Data
Controller in connection with the Services – e.g. personal data that forms part of, or
is disclosed by, a credential that the Data Subject submits to a Service for
verification. This may include special category data.
4. **Categories of Data Subjects.** As defined in the introduction to these Data Processing
Terms.
2. #### Data Processor’s Obligations [#data-processors-obligations]
1. **Processing in accordance with Data Controller’s instructions.** Data Processor will only
Process Personal Data on behalf of the Data Controller, and in accordance with the purpose
set out in clause 1.2 and otherwise in accordance with the terms of these Data Processing
Terms.
2. **Restrictions on Processing.** Except as set out in these Data Processing Terms, the Data
Processor is not entitled to Process the Personal Data for its own purposes.
3. **Technical and Organisation Security Measures.** Data Processor implements technical and
organisational security measures for the processing of personal data in accordance with the
GDPR. On written request, MATTR will provide the Data Controller with information reasonably
requested by Data Controller regarding security practices and policies.
4. **Data and security breach notification.** Data Processor will, as soon as practicable,
notify Data Controller about any breach of security resulting in the accidental or unlawful
disclosure of, or access to, Personal Data or any accidental or unauthorised access or any
other event affecting the integrity, availability or confidentiality of Personal Data in
accordance with clause 5.4 of the MATTR Customer Agreement.
5. **Reasonable assistance in response to enquiries.** Data Processor will provide reasonable
assistance in response to enquiries from Data Controller or the Regulator relating to Data
Processor’s Processing of Personal Data and abide by any specific advice of the Regulator to
Data Processor regarding the Processing of such Personal Data.
6. **Evidence of compliance with Data Processing Terms.** Data Processor will, upon written
request from Data Controller, provide Data Controller with all information reasonably
necessary to demonstrate Data Processor’s compliance with these Data Processing Terms.
7. **Reasonable assistance in connection with Applicable Data Protection Laws.** Data Processor
will provide reasonable assistance to Data Controller to enable that Data Controller to
comply with obligations which arise as a result of:
1. a Data Subject exercises their rights under Applicable Data Protection Law in respect of
Personal Data Processed by Data Processor on behalf of Data Controller (such as rights to
rectification, erasure, blocking, access their personal data, objection, restriction of
processing, data portability, and the right not to be subject to automated decision
making);
2. Data Controller is required to deal or comply with any assessment, enquiry, notice or
investigation by the Regulator; or
3. Data Controller is required under Applicable Data Protection Law to carry out a mandatory
data protection impact assessment or consult with the Regulator prior to Processing
Personal Data entrusted to the Data Processor under these Data Processing Terms,
8. **Audits.** Data Processor will permit Data Controller upon written notice, at a mutually
convenient date and time and no more than once per year (or more frequently if required by
law), to conduct an audit to confirm compliance with Data Processor’s obligations under these
Data Processing Terms. Such audits must be carried out subject to the auditor having
professional qualifications to carry out such an audit and agreeing to reasonable terms to
protect the confidential information of the Data Processor. Access to the systems and
processes of the Data Processor will be strictly limited to that which is necessary for the
purpose of this clause 2.8 and subject to access being within the Data Processor’s control.
9. **Processing in a Third Country.** Where the Data Processor Processes Personal Data in any
Third Country, it will ensure that any transfer of Personal Data to any Third Country will
comply with Applicable Data Protection Laws.
3. #### Data Controller’s Obligations [#data-controllers-obligations]
1. **Warranties.** Data Controller warrants that:
1. the legislation applicable to it does not prevent Data Processor from fulfilling the
instructions received from the Data Controller and performing Data Processor’s
obligations under the MATTR Customer Service Agreement and these Data Processing Terms;
and
2. it has complied and continues to comply with the Applicable Data Protection Laws, in
particular that it has obtained all necessary consents and given all necessary notices,
and otherwise has a legitimate ground to disclose the Personal Data to Data Processor and
enable the Processing of the Personal Data by the Data Processor as described in these
Data Processing Terms and the MATTR Customer Service Agreement.
2. **Indemnity.** Data Controller indemnifies and will hold harmless Data Processor on demand
from and against all claims, liabilities, costs, expenses, loss or damage (including
consequential losses, loss of profit and loss of reputation and all interests, penalties and
legal and other professional costs and expenses) incurred by Data Processor in connection
with any breach of this clause 3.
4. #### Sub-processors [#sub-processors]
1. **Permitted sub-processors.** Data Controller consents to the use of sub-processors for
Processing and as specified at [MATTR Data Sub-processors](/docs/resources/terms/data-sub-processors)
as updated from time to time. If Data Controller objects or does not agree to any such
sub-processors, the Data Processor may terminate the MATTR Customer Agreement on written
notice.
2. **Terms applicable to sub-processors.** Data Processor will ensure it has a written contract
in place with all sub-processors who perform Processing which contains obligations which
permit effective control and oversight with respect to the Processing of Personal Data to
ensure compliance with these Data Processing Terms.
5. #### Confidentiality [#confidentiality]
1. The Data Processor undertakes to the Data Controller to:
1. hold all Personal Data in strict confidence; and
2. ensure that employees, agents, officers, consultants, sub-processors, subcontractors and
advisers authorised to Process the Personal Data have committed themselves to
confidentiality or are under an appropriate statutory obligation of confidentiality.
2. The obligation in clause 5.1 will not apply to a disclosure of Personal Data that is required
by any law or regulation of any country with jurisdiction over the affairs of any Data
Processor or required by any order of any court of competent jurisdiction.
6. #### Governing Law and Jurisdiction [#governing-law-and-jurisdiction]
1. This Agreement will be governed by the Laws of New Zealand and each party consents to the
non-exclusive jurisdiction of the New Zealand courts. Neither party is permitted to object to
the transfer of any proceedings to New Zealand courts on any basis, including inconvenience.
7. #### Termination [#termination]
1. **Termination.** These Data Processing Terms terminate if the MATTR Customer Agreement
terminates.
2. **Consequences of termination.** Upon termination of the MATTR Customer Agreement and these
Data Processing Terms, the Data Processor will manage all Personal Data in accordance with
the MATTR Customer Agreement and Applicable Data Protection Laws, including with respect to
the destruction or return of such Personal Data, and the ongoing security of any retained
Personal Data.
8. #### Changes to Data Processing Terms [#changes-to-data-processing-terms]
1. Data Processor may make changes to these Data Processing Terms in accordance with the
processes set out in clause 17 of the MATTR Customer Agreement (as if references to “this
Agreement” were references to these Data Processing Terms).
9. #### Definitions and interpretation [#definitions-and-interpretation]
1. **Definitions.** The following terms will have the meaning set out below:
**Applicable Data Protection Laws** means the GDPR (as amended, consolidated, re-enacted or
replaced from time to time).
**GDPR** means Regulation 2016/679 of the European Parliament and of the Council of 27 April
2016 on the protection of natural persons with regard to the processing of personal data and
on the free movement of such data.
**MATTR Customer Agreement** means the Agreement entered into between you and MATTR Limited
as described in the Customer Agreement document (and including, for clarity, any applicable
Service Terms and Service Level Agreements).
**Personal Data** means any information relating to a Data Subject that is subject to the
GDPR or any legislation implementing the GDPR.
**Process, Processed or Processing** means any operation or set of operations which is
performed on Personal Data or on sets of Personal Data, whether or not by automated means,
such as collection, recording, organisation, structuring, storage, adaptation or alteration,
retrieval, consultation, use, disclosure by transmission, dissemination or otherwise making
available, alignment or combination, restriction, erasure or destruction.
**Regulator** means the data protection supervisory authority which has jurisdiction over
Data Controller’s Processing of Personal Data.
**Third Countries** means all countries outside of the scope of the data protection laws of
the EEA, excluding countries approved as providing adequate protection for Personal Data by
the European Commission from time to time, which at the date of these Data Processing Terms
include Andorra, Argentina, Canada, Faroe Islands, Guernsey, Isle of Man, Israel, Japan,
Jersey, New Zealand, Switzerland and Uruguay.
# MATTR Data Processing Terms - What's Changed
URL: /docs/resources/terms/data-processing-terms/recent-changes
Changes posted March 25, 2021
MATTR updated the [MATTR Data Processing Terms](/docs/resources/terms/data-processing-terms) on March
25, 2021.
The [MATTR Data Processing Terms](/docs/resources/terms/data-processing-terms) were published.
By continuing to use the Services or Materials after the effective date of any changes to the MATTR
Data Processing Terms, you agree to be bound by the modified terms.
# MATTR Data Sub-processors (archived)
URL: /docs/resources/terms/data-sub-processors/10-6-25
This document is provided for archival purposes only.
Last Updated: 10 June 2025
[See what's changed](/docs/resources/terms/data-sub-processors/recent-changes) |
[Previous versions](#previous-versions)
A data sub-processor is a service or capability hosted outside the core MATTR cloud environment that
may be used to process Customer Data, such as configuration data, logs, or master data.
Alternatively, it may be a capability employed to secure an environment where Customer Data is
stored. MATTR may engage the following entities to carry out specific data sub-processing
activities:
| Entity | Activity | Organization location |
| :----------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------- |
| Amazon Web Services, Inc. | Amazon Web Services (AWS) provides cloud infrastructure services from data centres based in a region agreed with MATTR customers. MATTR also uses other AWS products and services. | United States |
| Atlassian Pty Ltd | JIRA service desk is used for logging and managing incidents and service requests (which may contain come Customer data to support incident investigation). | Australia |
| DataDog | Datadog is used for monitoring the operational health of MATTR’s platforms. | United States |
| Doppler | Doppler is used for configuration management of MATTR GO Applications. | United States |
| GitHub | GitHub is used during the onboarding and access provisioning to MATTR platforms and applications. | United States |
| InnoCraft Limited (Matomo Analytics) | Matomo is used for website analytics tracking on MATTR websites. | Germany |
| Jotform | Jotform is used for form submission on MATTR websites. | United States |
| MAKE (previously Integromat) | MAKE is used for the provisioning of trial accounts. | Czech Republic |
| Microsoft (Office 365) | Office365 is a collaboration tool used for communication with customers and may store customer specific documentation. | United States |
| Microsoft (EntraID) | EntraID is the Identity provider for MATTR employees single sign-on across internal MATTR enterprise systems and cloud infrastructure. | Australia |
| Okta, Inc. | Okta is the Identity provider used to manage access to APIs and user accounts for access to the Self Service Portal. Deployed in the same (or adjacent) region to the MATTR cloud capability. | United States |
| Pardot (Salesforce Marketing Cloud) | Pardot is used for marketing automation and customer onboarding assistance. | United States |
| Salesforce, Inc. | Salesforce.com is used for sales and opportunity management and may store information about MATTR customers, prospective MATTR customers and Trial users. | United States |
| Sentry | Sentry is an error monitoring platform used by MATTR GO Mobile Applications. | United States |
| Slack Technologies, Inc. | Slack is a collaboration tool used for communication with customers and MATTR product support. | United States |
| Spark New Zealand Limited | Spark provides a Security Operations Centre (SOC) and Security Information and Event Management (SIEM) services. | New Zealand |
| Stripe, Inc. | Stripe supports online payments for MATTR customers. | United States |
| Sumo Logic, Inc. | Sumo Logic is a cloud-based log management capability used for audit and monitoring of MATTR’s capabilities. Deployed in the same (or adjacent) region to the MATTR cloud capability. | United States |
| Webflow | Webflow is a cloud-based development platform used by the MATTR website. | United States |
| Xero Limited | Xero is MATTR’s accounting software. | New Zealand |
| 6Sense | 6Sense is used for website analytics and sales and opportunity management. | United States |
For more information regarding data sub-processors (including more detailed
information regarding the type of data processed and the storage location)
please contact [privacy@mattr.global](mailto:privacy@mattr.global).
## Previous versions [#previous-versions]
* [8 Nov 2024](/docs/resources/terms/data-sub-processors/8-11-24)
* [28 May 2024](/docs/resources/terms/data-sub-processors/28-5-24)
* [25 Mar 2024](/docs/resources/terms/data-sub-processors/25-3-24)
* [16 May 2023](/docs/resources/terms/data-sub-processors/16-5-23)
# MATTR Data Sub-processors
URL: /docs/resources/terms/data-sub-processors/15-4-26
This document is provided for archival purposes only.
Last Updated: 15 April 2026
[See what's changed](/docs/resources/terms/data-sub-processors/recent-changes) |
[Previous versions](#previous-versions)
A data sub-processor is a service or capability hosted outside the core MATTR cloud environment that
may be used to process Customer Data, such as configuration data, logs, or master data.
Alternatively, it may be a capability employed to secure an environment where Customer Data is
stored. MATTR may engage the following entities to carry out specific data sub-processing
activities:
## MATTR as a Data Processor [#mattr-as-a-data-processor]
| Data Sub Processor | Data Processed | Data location | Relevant MATTR Products |
| :---------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------ |
| **Amazon Web Services, Inc.** | Amazon Web Services (AWS) provides cloud infrastructure services from data centres based in a region agreed with MATTR customers. MATTR also uses other AWS products and services. | Cloud region aligned **where possible** | MATTR VII MATTR Pi MATTR GO |
| **Atlassian Pty Ltd** | JIRA service desk is used for logging and managing incidents and service requests (which may contain some Customer data including personal information to support incident investigation). | Australia | MATTR VII MATTR Pi MATTR GO |
| **DataDog** | Datadog is used for monitoring the operational health of MATTR’s platforms. | United States | MATTR VII MATTR Pi MATTR GO |
| **Doppler** | Doppler is used for configuration management of MATTR GO mobile applications. | United States | MATTR GO |
| **GitHub** | GitHub is used to provide customers with access to specific MATTR capabilities such as SDKs. | United States | MATTR Pi |
| **Microsoft (EntraID)** | EntraID is the Identity provider for MATTR employees single sign-on across internal MATTR enterprise systems and cloud infrastructure. | Australia | MATTR VII MATTR Pi MATTR GO |
| **NPM** | NPM is used to provide customers with access to specific MATTR capabilities such as SDKs. | United States | MATTR Pi |
| **Okta, Inc.** | Okta is used as an Authorisation Server for MATTR VII APIs. Okta is also used as an IDP to support the MATTR Global Control Hub, including the Management APIs (M2M Clients) and MATTR Portal (Users), which are used to oversee and manage access to cloud environments. | The Okta Authorisation Server is Cloud region aligned **where possible**,
The Okta IDP (as part of MATTR Global Control Hub) is based in Australia. | MATTR VII MATTR Pi MATTR GO |
| **Red Canary** | Red Canary provides Security Operations Centre (SOC) and Managed Detection & Response (MDR) services. | United States | MATTR VII MATTR Pi MATTR GO |
| **Sentry** | Sentry is an error monitoring platform used by MATTR GO Mobile Applications and the MATTR Portal. | United States | MATTR VII MATTR GO |
| **Sumo Logic, Inc.** | Sumo Logic is a cloud-based log management capability used for audit and monitoring of MATTR’s capabilities. Deployed in the same (or adjacent) region to the MATTR cloud capability. | Cloud region aligned **where possible** | MATTR VII MATTR Pi MATTR GO |
## MATTR as a Data Controller [#mattr-as-a-data-controller]
| Data Sub Processor | Data Processed | Data Category | Data Location | Relevant MATTR Products |
| :--------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------- | :------------ | :------------------------------------ |
| **Docusign** | Used to electronically sign contractual information with MATTR customers | Customer Data - Ancillary | Australia | MATTR customers using Docusign |
| **HubSpot** | HubSpot stores and manages information about MATTR's customers for sales and opportunity management, and other operational functions. | Customer Data - Ancillary Customer Data - Representative Data | Australia | All MATTR customers |
| **InnoCraft Limited (Matomo Analytics)** | Matomo is used for website analytics tracking on MATTR websites. | Customer Data - Representative Data | Germany | MATTR customers using MATTR website |
| **Microsoft (Office 365)** | Office365 is a collaboration tool used for communication with customers and may store customer-specific documentation. | Customer Data - Ancillary | United States | All MATTR Customers |
| **Notion** | Used to store specific information including customer contact information for incident management and other repositories of operational information / designs etc. | Customer Data - Ancillary Customer Data - Representative Data | United States | All MATTR Customers |
| **Slack Technologies, Inc.** | Slack is a collaboration tool used for communication with customers and MATTR product support. Also used internally in the context of Incident Management. | Customer Data Customer Data - Personal Information | United States | All MATTR Customers |
| **Stripe, Inc.** | Stripe supports online payments for MATTR customers. | Customer Data | United States | Customers paying bills by credit card |
| **Webflow** | Webflow is a cloud-based development platform used by the MATTR website. | Customer Data - Representative Data | United States | MATTR customers using MATTR website |
| **Xero Limited** | Xero is MATTR’s accounting software and is used for invoicing and payments. | Customer Data - Ancillary | New Zealand | All MATTR Customers |
For more information regarding data sub-processors (including more detailed
information regarding the type of data processed and the storage location)
please contact [privacy@mattr.global](mailto:privacy@mattr.global).
## Previous versions [#previous-versions]
* [23 Mar 2026](/docs/resources/terms/data-sub-processors/23-3-26)
* [2 Feb 2026](/docs/resources/terms/data-sub-processors/2-2-26)
* [17 Oct 2025](/docs/resources/terms/data-sub-processors/17-10-25)
* [10 June 2025](/docs/resources/terms/data-sub-processors/10-6-25)
* [8 Nov 2024](/docs/resources/terms/data-sub-processors/8-11-24)
* [28 May 2024](/docs/resources/terms/data-sub-processors/28-5-24)
* [25 Mar 2024](/docs/resources/terms/data-sub-processors/25-3-24)
* [16 May 2023](/docs/resources/terms/data-sub-processors/16-5-23)
# MATTR Data Sub-processors (archived)
URL: /docs/resources/terms/data-sub-processors/16-5-23
This document is provided for archival purposes only.
Last Updated: 16 May 2023
[See what's changed](/docs/resources/terms/data-sub-processors/recent-changes) |
[Previous versions](#previous-versions)
MATTR may engage the following entities to carry out specific Personal Data Processing activities:
| Entity | Activity | Location |
| :----------------------------------- | :---------------------------------------------------------------------------------------------------------------------------- | :------------- |
| Amazon Web Services, Inc. | Amazon Web Services provides cloud infrastructure services from data centres based in a region agreed with MATTR’s customers. | United States |
| Atlassian Pty Ltd | Jira Service Desk is used by MATTR’s customers for logging and tracking of service requests and incidents. | Australia |
| InnoCraft Limited (Matomo Analytics) | Matomo may be used for website analytics tracking on MATTR websites. | New Zealand |
| MAKE (previously Integromat) | MAKE is used for the provisioning of trial accounts | Czech Republic |
| Mezmo, Inc. | Mezmo is a cloud-based log management capability used for audit and monitoring of MATTR’s capabilities. | United States |
| Microsoft | Office365 is a collaboration tool used for communication with customers and may store customer specific documents. | United States |
| Netlify, Inc. | Netlify is a cloud-based development platform used by the MATTR website and Self-Service capabilities. | United States |
| Okta, Inc. | Okta is an Identity provider used to manage access to MATTR APIs and self-service capabilities. | United States |
| Pardot (Salesforce Marketing Cloud) | Pardot is used for marketing automation and onboarding assistance. | United States |
| Salesforce, Inc. | Salesforce stores and manages information about MATTR’s customers for operational reasons (e.g. customer contacts) | United States |
| Slack Technologies, Inc. | Slack is a collaboration tool used for communication with customers and MATTR product support. | United States |
| Spark New Zealand Limited | Spark provides a Security Operations Centre (SOC) and Security Information and Event Management (SIEM) services. | New Zealand |
| Stripe, Inc. | Stripe supports online payments for MATTR customers. | United States |
| Sumo Logic, Inc. | Sumo Logic is a cloud-based log management capability used for audit and monitoring of MATTR’s capabilities. | United States |
| Xero Limited | Xero is MATTR’s accounting software. | New Zealand |
For more information regarding data sub-processors (including more detailed
information regarding the type of data processed and the storage location)
please contact [privacy@mattr.global](mailto:privacyx@mattr.global).
# MATTR Data Sub-processors
URL: /docs/resources/terms/data-sub-processors/17-10-25
This document is provided for archival purposes only.
Last Updated: 17 Oct 2025
[See what's changed](/docs/resources/terms/data-sub-processors/recent-changes) |
[Previous versions](#previous-versions)
A data sub-processor is a service or capability hosted outside the core MATTR cloud environment that
may be used to process Customer Data, such as configuration data, logs, or master data.
Alternatively, it may be a capability employed to secure an environment where Customer Data is
stored. MATTR may engage the following entities to carry out specific data sub-processing
activities:
## MATTR as a Data Processor [#mattr-as-a-data-processor]
| Data Sub Processor | Data Processed | Data location | Relevant MATTR Products |
| :---------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------- | :------------------------------------ |
| **Amazon Web Services, Inc.** | Amazon Web Services (AWS) provides cloud infrastructure services from data centres based in a region agreed with MATTR customers. MATTR also uses other AWS products and services. | Cloud region aligned **where possible** | MATTR VII MATTR Pi MATTR GO |
| **Atlassian Pty Ltd** | JIRA service desk is used for logging and managing incidents and service requests (which may contain some Customer data including personal information to support incident investigation). | Australia | MATTR VII MATTR Pi MATTR GO |
| **DataDog** | Datadog is used for monitoring the operational health of MATTR’s platforms. | United States | MATTR VII MATTR Pi MATTR GO |
| **Doppler** | Doppler is used for configuration management of MATTR GO mobile applications. | United States | MATTR GO |
| **GitHub** | GitHub is used to provide customers with access to specific MATTR capabilities such as SDKs | United States | MATTR Pi |
| **MATTR Control Plane** | Listed for completeness, MATTR operates a global control plane as a distributed management system, designed to oversee and manage all MATTR regional deployments worldwide. This control plane can be distributed across Australia, New Zealand, The United Kingdom, Canada, the United States or Germany. The global control plane does not store logs, customer data, or any sensitive information. | Australia Canada United Kingdom USA Germany | MATTR VII MATTR Pi MATTR GO |
| **Microsoft (EntraID)** | EntraID is the Identity provider for MATTR employees single sign-on across internal MATTR enterprise systems and cloud infrastructure. | Australia | MATTR VII MATTR Pi MATTR GO |
| **NPM** | NPM is used to provide customers with access to specific MATTR capabilities such as SDKs. | United States | MATTR Pi |
| **Okta, Inc.** | Okta is the Identity provider used to manage access to APIs and user accounts (for access to the MATTR Portal). Deployed in the same (or adjacent) region to the MATTR cloud capability. | Cloud region aligned **where possible** | MATTR VII MATTR Pi MATTR GO |
| **Sentry** | Sentry is an error monitoring platform used by MATTR GO Mobile Applications. | United States | MATTR GO |
| **Spark New Zealand Limited** | Spark provides a Security Operations Centre (SOC) and Security Information and Event Management (SIEM) services. | New Zealand | MATTR VII MATTR Pi MATTR GO |
| **Sumo Logic, Inc.** | Sumo Logic is a cloud-based log management capability used for audit and monitoring of MATTR’s capabilities. Deployed in the same (or adjacent) region to the MATTR cloud capability. | Cloud region aligned **where possible** | MATTR VII MATTR Pi MATTR GO |
## MATTR as a Data Controller [#mattr-as-a-data-controller]
| Data Sub Processor | Data Processed | Data Category | Data Location | Relevant MATTR Products |
| :--------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------- | :------------ | :------------------------------------ |
| **Docusign** | Used to electronically sign contractual information with MATTR customers | Customer Data - Ancillary | Australia | MATTR customers using Docusign |
| **InnoCraft Limited (Matomo Analytics)** | Matomo is used for website analytics tracking on MATTR websites. | Customer Data - Representative Data | Germany | MATTR customers using MATTR website |
| **Jotform** | Jotform is used for form submission on MATTR websites. | Customer Data - Representative Data | United States | MATTR customers using MATTR website |
| **Microsoft (Office 365)** | Office365 is a collaboration tool used for communication with customers and may store customer specific documentation. | Customer Data - Ancillary | United States | All MATTR Customers |
| **Notion** | Used to store specific information including customer contact information for incident management and other repositories of operational information / designs etc. | Customer Data - Ancillary Customer Data - Representative Data | United States | All MATTR Customers |
| **Slack Technologies, Inc.** | Slack is a collaboration tool used for communication with customers and MATTR product support. Also used internally in the context of Incident Management. | Customer Data Customer Data - Personal Information | United States | All MATTR Customers |
| **Stripe, Inc.** | Stripe supports online payments for MATTR customers. | Customer Data | United States | Customers paying bills by credit card |
| **Webflow** | Webflow is a cloud-based development platform used by the MATTR website. | Customer Data - Representative Data | United States | MATTR customers using MATTR website |
| **Xero Limited** | Xero is MATTR’s accounting software and is used for invoicing and payments. | Customer Data - Ancillary | New Zealand | All MATTR Customers |
For more information regarding data sub-processors (including more detailed
information regarding the type of data processed and the storage location)
please contact [privacy@mattr.global](mailto:privacy@mattr.global).
## Previous versions [#previous-versions]
* [10 June 2025](/docs/resources/terms/data-sub-processors/10-6-25)
* [8 Nov 2024](/docs/resources/terms/data-sub-processors/8-11-24)
* [28 May 2024](/docs/resources/terms/data-sub-processors/28-5-24)
* [25 Mar 2024](/docs/resources/terms/data-sub-processors/25-3-24)
* [16 May 2023](/docs/resources/terms/data-sub-processors/16-5-23)
# MATTR Data Sub-processors
URL: /docs/resources/terms/data-sub-processors/2-2-26
This document is provided for archival purposes only.
Last Updated: 2 Feb 2026
[See what's changed](/docs/resources/terms/data-sub-processors/recent-changes) |
[Previous versions](#previous-versions)
A data sub-processor is a service or capability hosted outside the core MATTR cloud environment that
may be used to process Customer Data, such as configuration data, logs, or master data.
Alternatively, it may be a capability employed to secure an environment where Customer Data is
stored. MATTR may engage the following entities to carry out specific data sub-processing
activities:
## MATTR as a Data Processor [#mattr-as-a-data-processor]
| Data Sub Processor | Data Processed | Data location | Relevant MATTR Products |
| :---------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------- | :------------------------------------ |
| **Amazon Web Services, Inc.** | Amazon Web Services (AWS) provides cloud infrastructure services from data centres based in a region agreed with MATTR customers. MATTR also uses other AWS products and services. | Cloud region aligned **where possible** | MATTR VII MATTR Pi MATTR GO |
| **Atlassian Pty Ltd** | JIRA service desk is used for logging and managing incidents and service requests (which may contain some Customer data including personal information to support incident investigation). | Australia | MATTR VII MATTR Pi MATTR GO |
| **DataDog** | Datadog is used for monitoring the operational health of MATTR's platforms. | United States | MATTR VII MATTR Pi MATTR GO |
| **Doppler** | Doppler is used for configuration management of MATTR GO mobile applications. | United States | MATTR GO |
| **GitHub** | GitHub is used to provide customers with access to specific MATTR capabilities such as SDKs. | United States | MATTR Pi |
| **MATTR Control Plane** | Listed for completeness, MATTR operates a global control plane as a distributed management system, designed to oversee and manage all MATTR regional deployments worldwide. This control plane can be distributed across Australia, New Zealand, The United Kingdom, Canada, the United States or Germany. The global control plane does not store logs, customer data, or any sensitive information. | Australia Canada United Kingdom USA Germany | MATTR VII MATTR Pi MATTR GO |
| **Microsoft (EntraID)** | EntraID is the Identity provider for MATTR employees single sign-on across internal MATTR enterprise systems and cloud infrastructure. | Australia | MATTR VII MATTR Pi MATTR GO |
| **NPM** | NPM is used to provide customers with access to specific MATTR capabilities such as SDKs. | United States | MATTR Pi |
| **Okta, Inc.** | Okta is the Identity provider used to manage access to APIs and user accounts (for access to the MATTR Portal). Deployed in the same (or adjacent) region to the MATTR cloud capability. | Cloud region aligned **where possible** | MATTR VII MATTR Pi MATTR GO |
| **Sentry** | Sentry is an error monitoring platform used by MATTR GO Mobile Applications and the MATTR Portal. | United States | MATTR VII MATTR GO |
| **Spark New Zealand Limited** | Spark provides a Security Operations Centre (SOC) and Security Information and Event Management (SIEM) services. | New Zealand | MATTR VII MATTR Pi MATTR GO |
| **Sumo Logic, Inc.** | Sumo Logic is a cloud-based log management capability used for audit and monitoring of MATTR's capabilities. Deployed in the same (or adjacent) region to the MATTR cloud capability. | Cloud region aligned **where possible** | MATTR VII MATTR Pi MATTR GO |
## MATTR as a Data Controller [#mattr-as-a-data-controller]
| Data Sub Processor | Data Processed | Data Category | Data Location | Relevant MATTR Products |
| :--------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------- | :------------ | :------------------------------------ |
| **Docusign** | Used to electronically sign contractual information with MATTR customers | Customer Data - Ancillary | Australia | MATTR customers using Docusign |
| **InnoCraft Limited (Matomo Analytics)** | Matomo is used for website analytics tracking on MATTR websites. | Customer Data - Representative Data | Germany | MATTR customers using MATTR website |
| **Jotform** | Jotform is used for form submission on MATTR websites. | Customer Data - Representative Data | United States | MATTR customers using MATTR website |
| **Microsoft (Office 365)** | Office365 is a collaboration tool used for communication with customers and may store customer specific documentation. | Customer Data - Ancillary | United States | All MATTR Customers |
| **Notion** | Used to store specific information including customer contact information for incident management and other repositories of operational information / designs etc. | Customer Data - Ancillary Customer Data - Representative Data | United States | All MATTR Customers |
| **Slack Technologies, Inc.** | Slack is a collaboration tool used for communication with customers and MATTR product support. Also used internally in the context of Incident Management. | Customer Data Customer Data - Personal Information | United States | All MATTR Customers |
| **Stripe, Inc.** | Stripe supports online payments for MATTR customers. | Customer Data | United States | Customers paying bills by credit card |
| **Webflow** | Webflow is a cloud-based development platform used by the MATTR website. | Customer Data - Representative Data | United States | MATTR customers using MATTR website |
| **Xero Limited** | Xero is MATTR's accounting software and is used for invoicing and payments. | Customer Data - Ancillary | New Zealand | All MATTR Customers |
For more information regarding data sub-processors (including more detailed
information regarding the type of data processed and the storage location)
please contact [privacy@mattr.global](mailto:privacy@mattr.global).
## Previous versions [#previous-versions]
* [17 Oct 2025](/docs/resources/terms/data-sub-processors/17-10-25)
* [10 June 2025](/docs/resources/terms/data-sub-processors/10-6-25)
* [8 Nov 2024](/docs/resources/terms/data-sub-processors/8-11-24)
* [28 May 2024](/docs/resources/terms/data-sub-processors/28-5-24)
* [25 Mar 2024](/docs/resources/terms/data-sub-processors/25-3-24)
* [16 May 2023](/docs/resources/terms/data-sub-processors/16-5-23)
# MATTR Data Sub-processors
URL: /docs/resources/terms/data-sub-processors/23-3-26
This document is provided for archival purposes only.
Last Updated: 23 March 2026
[See what's changed](/docs/resources/terms/data-sub-processors/recent-changes) |
[Previous versions](#previous-versions)
A data sub-processor is a service or capability hosted outside the core MATTR cloud environment that
may be used to process Customer Data, such as configuration data, logs, or master data.
Alternatively, it may be a capability employed to secure an environment where Customer Data is
stored. MATTR may engage the following entities to carry out specific data sub-processing
activities:
## MATTR as a Data Processor [#mattr-as-a-data-processor]
| Data Sub Processor | Data Processed | Data location | Relevant MATTR Products |
| :---------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------- | :------------------------------------ |
| **Amazon Web Services, Inc.** | Amazon Web Services (AWS) provides cloud infrastructure services from data centres based in a region agreed with MATTR customers. MATTR also uses other AWS products and services. | Cloud region aligned **where possible** | MATTR VII MATTR Pi MATTR GO |
| **Atlassian Pty Ltd** | JIRA service desk is used for logging and managing incidents and service requests (which may contain some Customer data including personal information to support incident investigation). | Australia | MATTR VII MATTR Pi MATTR GO |
| **DataDog** | Datadog is used for monitoring the operational health of MATTR's platforms. | United States | MATTR VII MATTR Pi MATTR GO |
| **Doppler** | Doppler is used for configuration management of MATTR GO mobile applications. | United States | MATTR GO |
| **GitHub** | GitHub is used to provide customers with access to specific MATTR capabilities such as SDKs. | United States | MATTR Pi |
| **MATTR Control Plane** | Listed for completeness, MATTR operates a global control plane as a distributed management system, designed to oversee and manage all MATTR regional deployments worldwide. This control plane can be distributed across Australia, New Zealand, The United Kingdom, Canada, the United States or Germany. The global control plane does not store logs, customer data, or any sensitive information. | Australia Canada United Kingdom USA Germany | MATTR VII MATTR Pi MATTR GO |
| **Microsoft (EntraID)** | EntraID is the Identity provider for MATTR employees single sign-on across internal MATTR enterprise systems and cloud infrastructure. | Australia | MATTR VII MATTR Pi MATTR GO |
| **NPM** | NPM is used to provide customers with access to specific MATTR capabilities such as SDKs. | United States | MATTR Pi |
| **Okta, Inc.** | Okta is the Identity provider used to manage access to APIs and user accounts (for access to the MATTR Portal). Deployed in the same (or adjacent) region to the MATTR cloud capability. | Cloud region aligned **where possible** | MATTR VII MATTR Pi MATTR GO |
| **Red Canary** | Red Canary provides Security Operations Centre (SOC) and Managed Detection & Response (MDR) services. | United States | MATTR VII MATTR Pi MATTR GO |
| **Sentry** | Sentry is an error monitoring platform used by MATTR GO Mobile Applications and the MATTR Portal. | United States | MATTR VII MATTR GO |
| **Sumo Logic, Inc.** | Sumo Logic is a cloud-based log management capability used for audit and monitoring of MATTR's capabilities. Deployed in the same (or adjacent) region to the MATTR cloud capability. | Cloud region aligned **where possible** | MATTR VII MATTR Pi MATTR GO |
## MATTR as a Data Controller [#mattr-as-a-data-controller]
| Data Sub Processor | Data Processed | Data Category | Data Location | Relevant MATTR Products |
| :--------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------- | :------------ | :------------------------------------ |
| **Docusign** | Used to electronically sign contractual information with MATTR customers | Customer Data - Ancillary | Australia | MATTR customers using Docusign |
| **HubSpot** | HubSpot stores and manages information about MATTR's customers for sales and opportunity management, and other operational functions. | Customer Data - Ancillary Customer Data - Representative Data | Australia | All MATTR customers |
| **InnoCraft Limited (Matomo Analytics)** | Matomo is used for website analytics tracking on MATTR websites. | Customer Data - Representative Data | Germany | MATTR customers using MATTR website |
| **Jotform** | Jotform is used for form submission on MATTR websites. | Customer Data - Representative Data | United States | MATTR customers using MATTR website |
| **Microsoft (Office 365)** | Office365 is a collaboration tool used for communication with customers and may store customer-specific documentation. | Customer Data - Ancillary | United States | All MATTR Customers |
| **Notion** | Used to store specific information including customer contact information for incident management and other repositories of operational information / designs etc. | Customer Data - Ancillary Customer Data - Representative Data | United States | All MATTR Customers |
| **Slack Technologies, Inc.** | Slack is a collaboration tool used for communication with customers and MATTR product support. Also used internally in the context of Incident Management. | Customer Data Customer Data - Personal Information | United States | All MATTR Customers |
| **Stripe, Inc.** | Stripe supports online payments for MATTR customers. | Customer Data | United States | Customers paying bills by credit card |
| **Webflow** | Webflow is a cloud-based development platform used by the MATTR website. | Customer Data - Representative Data | United States | MATTR customers using MATTR website |
| **Xero Limited** | Xero is MATTR's accounting software and is used for invoicing and payments. | Customer Data - Ancillary | New Zealand | All MATTR Customers |
For more information regarding data sub-processors (including more detailed
information regarding the type of data processed and the storage location)
please contact [privacy@mattr.global](mailto:privacy@mattr.global).
## Previous versions [#previous-versions]
* [2 Feb 2026](/docs/resources/terms/data-sub-processors/2-2-26)
* [17 Oct 2025](/docs/resources/terms/data-sub-processors/17-10-25)
* [10 June 2025](/docs/resources/terms/data-sub-processors/10-6-25)
* [8 Nov 2024](/docs/resources/terms/data-sub-processors/8-11-24)
* [28 May 2024](/docs/resources/terms/data-sub-processors/28-5-24)
* [25 Mar 2024](/docs/resources/terms/data-sub-processors/25-3-24)
* [16 May 2023](/docs/resources/terms/data-sub-processors/16-5-23)
# MATTR Data Sub-processors (archived)
URL: /docs/resources/terms/data-sub-processors/25-3-24
This document is provided for archival purposes only.
Last Updated: 25 Mar 2024
[See what's changed](/docs/resources/terms/data-sub-processors/recent-changes) |
[Previous versions](#previous-versions)
MATTR may engage the following entities to carry out specific Personal Data Processing activities:
| Entity | Activity | Location |
| :----------------------------------- | :---------------------------------------------------------------------------------------------------------------------------- | :------------- |
| Amazon Web Services, Inc. | Amazon Web Services provides cloud infrastructure services from data centres based in a region agreed with MATTR’s customers. | United States |
| Atlassian Pty Ltd | Jira Service Desk is used by MATTR’s customers for logging and tracking of service requests and incidents. | Australia |
| InnoCraft Limited (Matomo Analytics) | Matomo may be used for website analytics tracking on MATTR websites. | New Zealand |
| MAKE (previously Integromat) | MAKE is used for the provisioning of trial accounts | Czech Republic |
| Microsoft | Office365 is a collaboration tool used for communication with customers and may store customer specific documents. | United States |
| Okta, Inc. | Okta is an Identity provider used to manage access to MATTR APIs and self-service capabilities. | United States |
| Pardot (Salesforce Marketing Cloud) | Pardot is used for marketing automation and onboarding assistance. | United States |
| Salesforce, Inc. | Salesforce stores and manages information about MATTR’s customers for operational reasons (e.g. customer contacts) | United States |
| Slack Technologies, Inc. | Slack is a collaboration tool used for communication with customers and MATTR product support. | United States |
| Spark New Zealand Limited | Spark provides a Security Operations Centre (SOC) and Security Information and Event Management (SIEM) services. | New Zealand |
| Stripe, Inc. | Stripe supports online payments for MATTR customers. | United States |
| Sumo Logic, Inc. | Sumo Logic is a cloud-based log management capability used for audit and monitoring of MATTR’s capabilities. | United States |
| Xero Limited | Xero is MATTR’s accounting software. | New Zealand |
For more information regarding data sub-processors (including more detailed
information regarding the type of data processed and the storage location)
please contact [privacy@mattr.global](mailto:privacyx@mattr.global).
## Previous versions [#previous-versions]
* [16 May 2023](/docs/resources/terms/data-sub-processors/16-5-23)
# MATTR Data Sub-processors (archived)
URL: /docs/resources/terms/data-sub-processors/28-5-24
This document is provided for archival purposes only.
Last Updated: 28 May 2024
[See what's changed](/docs/resources/terms/data-sub-processors/recent-changes) |
[Previous versions](#previous-versions)
MATTR may engage the following entities to carry out specific Personal Data Processing activities:
| Entity | Activity | Location |
| :----------------------------------- | :---------------------------------------------------------------------------------------------------------------------------- | :------------- |
| Amazon Web Services, Inc. | Amazon Web Services provides cloud infrastructure services from data centres based in a region agreed with MATTR’s customers. | United States |
| Atlassian Pty Ltd | Jira Service Desk is used by MATTR’s customers for logging and tracking of service requests and incidents. | Australia |
| InnoCraft Limited (Matomo Analytics) | Matomo may be used for website analytics tracking on MATTR websites. | New Zealand |
| Jotform | Jotform is used for form submissions on MATTR websites. | United States |
| MAKE (previously Integromat) | MAKE is used for the provisioning of trial accounts | Czech Republic |
| Microsoft | Office365 is a collaboration tool used for communication with customers and may store customer specific documents. | United States |
| Okta, Inc. | Okta is an Identity provider used to manage access to MATTR APIs and self-service capabilities. | United States |
| Pardot (Salesforce Marketing Cloud) | Pardot is used for marketing automation and onboarding assistance. | United States |
| Salesforce, Inc. | Salesforce stores and manages information about MATTR’s customers for operational reasons (e.g. customer contacts) | United States |
| Slack Technologies, Inc. | Slack is a collaboration tool used for communication with customers and MATTR product support. | United States |
| Spark New Zealand Limited | Spark provides a Security Operations Centre (SOC) and Security Information and Event Management (SIEM) services. | New Zealand |
| Stripe, Inc. | Stripe supports online payments for MATTR customers. | United States |
| Sumo Logic, Inc. | Sumo Logic is a cloud-based log management capability used for audit and monitoring of MATTR’s capabilities. | United States |
| Xero Limited | Xero is MATTR’s accounting software. | New Zealand |
For more information regarding data sub-processors (including more detailed
information regarding the type of data processed and the storage location)
please contact [privacy@mattr.global](mailto:privacyx@mattr.global).
## Previous versions [#previous-versions]
* [25 Mar 2024](/docs/resources/terms/data-sub-processors/25-3-24)
* [16 May 2023](/docs/resources/terms/data-sub-processors/16-5-23)
# MATTR Data Sub-processors (archived)
URL: /docs/resources/terms/data-sub-processors/8-11-24
This document is provided for archival purposes only.
Last Updated: 8 Nov 2024
[See what's changed](/docs/resources/terms/data-sub-processors/recent-changes) |
[Previous versions](#previous-versions)
A data sub-processor is a service or capability hosted outside the core MATTR cloud environment that
may be used to process Customer Data, such as configuration data, logs, or master data.
Alternatively, it may be a capability employed to secure an environment where Customer Data is
stored. MATTR may engage the following entities to carry out specific data sub-processing
activities:
| Entity | Activity | Organisation location |
| :----------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------- |
| Amazon Web Services, Inc. | Amazon Web Services (AWS) provides cloud infrastructure services from data centres based in a region agreed with MATTR customers. MATTR also uses other AWS products and services. | United States |
| Atlassian Pty Ltd | JIRA service desk is used for logging and managing incidents and service requests (which may contain come Customer data to support incident investigation). | Australia |
| DataDog | Datadog is used for monitoring the operational health of MATTR’s platforms. | United States |
| Doppler | Doppler is used for configuration management of MATTR GO Applications. | United States |
| InnoCraft Limited (Matomo Analytics) | Matomo is used for website analytics tracking on MATTR websites. | Germany |
| Jotform | Jotform is used for form submission on MATTR websites. | United States |
| MAKE (previously Integromat) | MAKE is used for the provisioning of trial accounts. | Czech Republic |
| Microsoft (Office 365) | Office365 is a collaboration tool used for communication with customers and may store customer specific documentation. | United States |
| Microsoft (EntraID) | EntraID is the Identity provider for MATTR employees single sign-on across internal MATTR enterprise systems and cloud infrastructure. | Australia |
| Okta, Inc. | Okta is the Identity provider used to manage access to APIs and user accounts for access to the Self Service Portal. Deployed in the same (or adjacent) region to the MATTR cloud capability. | United States |
| Pardot (Salesforce Marketing Cloud) | Pardot is used for marketing automation and customer onboarding assistance. | United States |
| Salesforce, Inc. | Salesforce.com is used for sales and opportunity management and may store information about MATTR customers, prospective MATTR customers and Trial users. | United States |
| Sentry | Sentry is an error monitoring platform used by MATTR GO Mobile Applications. | United States |
| Slack Technologies, Inc. | Slack is a collaboration tool used for communication with customers and MATTR product support. | United States |
| Spark New Zealand Limited | Spark provides a Security Operations Centre (SOC) and Security Information and Event Management (SIEM) services. | New Zealand |
| Stripe, Inc. | Stripe supports online payments for MATTR customers. | United States |
| Sumo Logic, Inc. | Sumo Logic is a cloud-based log management capability used for audit and monitoring of MATTR’s capabilities. Deployed in the same (or adjacent) region to the MATTR cloud capability. | United States |
| Webflow | Webflow is a cloud-based development platform used by the MATTR website. | United States |
| Xero Limited | Xero is MATTR’s accounting software. | New Zealand |
| 6Sense | 6Sense is used for website analytics and sales and opportunity management. | United States |
For more information regarding data sub-processors (including more detailed
information regarding the type of data processed and the storage location)
please contact [privacy@mattr.global](mailto:privacyx@mattr.global).
## Previous versions [#previous-versions]
* [28 May 2024](/docs/resources/terms/data-sub-processors/28-5-24)
* [25 Mar 2024](/docs/resources/terms/data-sub-processors/25-3-24)
* [16 May 2023](/docs/resources/terms/data-sub-processors/16-5-23)
# MATTR Data Sub-processors
URL: /docs/resources/terms/data-sub-processors
Last Updated: 12 June 2026
[See what's changed](/docs/resources/terms/data-sub-processors/recent-changes) |
[Previous versions](#previous-versions)
A data sub-processor is a service or capability hosted outside the core MATTR cloud environment that
may be used to process Customer Data, such as configuration data, logs, or master data.
Alternatively, it may be a capability employed to secure an environment where Customer Data is
stored. MATTR may engage the following entities to carry out specific data sub-processing
activities:
## MATTR as a Data Processor [#mattr-as-a-data-processor]
| Data Sub Processor | Data Processed | Data location | Relevant MATTR Products |
| :------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------ |
| **Amazon Web Services, Inc.** | Amazon Web Services (AWS) provides cloud infrastructure services from data centres based in a region agreed with MATTR customers. MATTR also uses other AWS products and services. | Cloud region aligned **where possible** | MATTR VII MATTR Pi MATTR GO |
| **Atlassian Pty Ltd** | JIRA service desk is used for logging and managing incidents and service requests (which may contain some Customer data including personal information to support incident investigation). | Australia | MATTR VII MATTR Pi MATTR GO |
| **Checkly Inc. (and Checkly GmbH)** | Checkly is used for monitoring the operational health of MATTR’s platforms. | Cloud region aligned **where possible** | MATTR VII MATTR Pi MATTR GO |
| **Doppler Technologies, Inc.** | Doppler is used for configuration management of MATTR GO mobile applications. | United States | MATTR GO |
| **GitHub, Inc.** | GitHub is used to provide customers with access to specific MATTR capabilities such as SDKs. | United States | MATTR Pi |
| **Microsoft Corporation (EntraID)** | EntraID is the Identity provider for MATTR employees single sign-on across internal MATTR enterprise systems and cloud infrastructure. | Australia | MATTR VII MATTR Pi MATTR GO |
| **npm, Inc.** | npm is used to provide customers with access to specific MATTR capabilities such as SDKs. | United States | MATTR Pi |
| **Okta, Inc.** | Okta is used as an Authorisation Server for MATTR VII APIs. Okta is also used as an IDP to support the MATTR Global Control Hub, including the Management APIs (M2M Clients) and MATTR Portal (Users), which are used to oversee and manage access to cloud environments. | The Okta Authorisation Server is Cloud region aligned **where possible**,
The Okta IDP (as part of MATTR Global Control Hub) is based in Australia. | MATTR VII MATTR Pi MATTR GO |
| **Red Canary, Inc.** | Red Canary provides Security Operations Centre (SOC) and Managed Detection & Response (MDR) services. | United States | MATTR VII MATTR Pi MATTR GO |
| **Sentry (Functional Software, Inc.)** | Sentry is an error monitoring platform used by MATTR GO Mobile Applications and the MATTR Portal. | United States | MATTR VII MATTR GO |
| **Sumo Logic, Inc.** | Sumo Logic is a cloud-based log management capability used for audit and monitoring of MATTR’s capabilities. Deployed in the same (or adjacent) region to the MATTR cloud capability. | Cloud region aligned **where possible** | MATTR VII MATTR Pi MATTR GO |
## MATTR as a Data Controller [#mattr-as-a-data-controller]
| Data Sub Processor | Data Processed | Data Category | Data Location | Relevant MATTR Products |
| :--------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------- | :------------ | :------------------------------------ |
| **Docusign, Inc.** | Used to electronically sign contractual information with MATTR customers | Customer Data - Ancillary | Australia | MATTR customers using Docusign |
| **HubSpot, Inc.** | HubSpot stores and manages information about MATTR's customers for sales and opportunity management, and other operational functions. | Customer Data - Ancillary Customer Data - Representative Data | Australia | All MATTR customers |
| **InnoCraft Limited (Matomo Analytics)** | Matomo is used for website analytics tracking on MATTR websites. | Customer Data - Representative Data | Germany | MATTR customers using MATTR website |
| **Microsoft Corporation (Office 365)** | Office 365 is a collaboration tool used for communication with customers and may store customer-specific documentation. | Customer Data - Ancillary | United States | All MATTR Customers |
| **Notion Labs, Inc.** | Used to store specific information including customer contact information for incident management and other repositories of operational information / designs etc. | Customer Data - Ancillary Customer Data - Representative Data | United States | All MATTR Customers |
| **Slack Technologies, LLC** | Slack is a collaboration tool used for communication with customers and MATTR product support. Also used internally in the context of Incident Management. | Customer Data Customer Data - Personal Information | United States | All MATTR Customers |
| **Stripe, LLC** | Stripe supports online payments for MATTR customers. | Customer Data | United States | Customers paying bills by credit card |
| **Webflow, Inc.** | Webflow is a cloud-based development platform used by the MATTR website. | Customer Data - Representative Data | United States | MATTR customers using MATTR website |
| **Xero Limited** | Xero is MATTR’s accounting software and is used for invoicing and payments. | Customer Data - Ancillary | New Zealand | All MATTR Customers |
For more information regarding data sub-processors (including more detailed
information regarding the type of data processed and the storage location)
please contact [privacy@mattr.global](mailto:privacy@mattr.global).
## Previous versions [#previous-versions]
* [15 Apr 2026](/docs/resources/terms/data-sub-processors/15-4-26)
* [23 Mar 2026](/docs/resources/terms/data-sub-processors/23-3-26)
* [2 Feb 2026](/docs/resources/terms/data-sub-processors/2-2-26)
* [17 Oct 2025](/docs/resources/terms/data-sub-processors/17-10-25)
* [10 June 2025](/docs/resources/terms/data-sub-processors/10-6-25)
* [8 Nov 2024](/docs/resources/terms/data-sub-processors/8-11-24)
* [28 May 2024](/docs/resources/terms/data-sub-processors/28-5-24)
* [25 Mar 2024](/docs/resources/terms/data-sub-processors/25-3-24)
* [16 May 2023](/docs/resources/terms/data-sub-processors/16-5-23)
# MATTR Data Sub-processors - What's Changed
URL: /docs/resources/terms/data-sub-processors/recent-changes
## Changes posted June 12, 2026 [#changes-posted-june-12-2026]
MATTR updated the [MATTR Data Sub-processors](/docs/resources/terms/data-sub-processors) on June 12, 2026:
* Removal of Datadog
* Addition of Checkly Inc. (and Checkly GmbH)
* Update of sub-processor names to reflect their registered entity names
## Changes posted April 15, 2026 [#changes-posted-april-15-2026]
MATTR updated the [MATTR Data Sub-processors](/docs/resources/terms/data-sub-processors) on April 15, 2026:
* Removal of the MATTR Control Plane entry
* Removal of the Jotform entry
* Updated Okta, Inc. entry to reflect its dual role as an Authorisation Server for MATTR VII APIs and as an IDP for the MATTR Global Control Hub
## Changes posted March 23, 2026 [#changes-posted-march-23-2026]
MATTR updated the [MATTR Data Sub-processors](/docs/resources/terms/data-sub-processors) on March 23, 2026:
* Removal of Spark New Zealand Limited
* Addition of Red Canary
* Addition of HubSpot
## Changes posted Feb 2, 2026 [#changes-posted-feb-2-2026]
MATTR updated the [MATTR Data Sub-processors](/docs/resources/terms/data-sub-processors) on February 2, 2026:
* Updated the Sentry Data Sub-processor entry to reflect its use in the MATTR Portal.
## Changes posted Oct 17, 2025 [#changes-posted-oct-17-2025]
MATTR updated the [MATTR Data Sub-processors](/docs/resources/terms/data-sub-processors) on October 17, 2025:
* Split list based on data usage pattern (MATTR as a Data Controller vs Data Processor)
* Removal of the following Data Sub-processors:
* 6Sense
* Salesforce
* Pardot
## Changes posted June 10, 2025 [#changes-posted-june-10-2025]
MATTR updated the [MATTR Data Sub-processors](/docs/resources/terms/data-sub-processors) on June 10, 2025:
Addition of the following Data Sub-processors:
* GitHub
## Changes posted Nov 8, 2024 [#changes-posted-nov-8-2024]
MATTR updated the [MATTR Data Sub-processors](/docs/resources/terms/data-sub-processors) on Nov 8, 2024:
Addition of the following Data Sub-processors:
* DataDog
* Doppler
* Microsoft (EntraID)
* Sentry
* Webflow
* 6Sense
## Changes posted May 28, 2024 [#changes-posted-may-28-2024]
MATTR updated the [MATTR Data Sub-processors](/docs/resources/terms/data-sub-processors) on May 28, 2024:
* Addition of Jotform
## Changes posted Mar 25, 2024 [#changes-posted-mar-25-2024]
MATTR updated the [MATTR Data Sub-processors](/docs/resources/terms/data-sub-processors) on March 25, 2024.
* Removal of Mezmo Inc.
* Removal of Netlify Inc.
## Changes posted May 16, 2023 [#changes-posted-may-16-2023]
MATTR updated the [MATTR Data Sub-processors](/docs/resources/terms/data-sub-processors) on May 16, 2023.
* Addition of SumoLogic Inc, MAKE and Okta Inc.
* Removal of Google.
## Changes posted December 9, 2021 [#changes-posted-december-9-2021]
MATTR updated the [MATTR Data Sub-processors](/docs/resources/terms/data-sub-processors) on December 9, 2021.
* Addition of Matomo Analytics, Pardot and Google.
## Changes posted March 25, 2021 [#changes-posted-march-25-2021]
MATTR updated the [MATTR Data Sub-processors](/docs/resources/terms/data-sub-processors) on March 25, 2021.
The [MATTR Data Sub-processors](/docs/resources/terms/data-sub-processors) was published.
Your use of our Services after the effective date of this update confirms that you have read and
understood the updated MATTR Data Sub-processors.
# Mobile Wallet Privacy Policy (archived)
URL: /docs/resources/terms/go-applications/15-9-20
This document is provided for archival purposes only.
Last Updated: 15 September 2020
Your privacy is important to us, and we take it seriously. This privacy policy (**Privacy Policy**)
explains how we collect, use and disclose personal information in relation to your use of the MATTR
Mobile Wallet (“**Mobile Wallet**“) and areas of connection with the MATTR Platform
(“**Platform**“).
As a New Zealand based company, we comply with the New Zealand Privacy Act 1993 (**the Act**) when
dealing with personal information. This Privacy Policy does not limit or exclude any of your rights
under the Act.\
You can find more information about the Act and your rights at
[www.privacy.org.nz](https://www.privacy.org.nz).
When we say “**our**,” “**we**,” or “**us**,” we are talking about MATTR Limited (**MATTR**). When
we say “**you**” or “**your**” we are referring to you, the end-user of the Mobile Wallet.
1. #### General [#general]
We may collect data from you either through our interactions with you directly, or through your
use of the Mobile Wallet. The information we collect depends on the context of your interactions
and the choices you make, including your choice of privacy settings and features you use.
The Mobile Wallet is designed to enable you to obtain, store, and share digital credentials – a
little bit like a ‘digital card holder’ for credentials that might otherwise be presented in a
physical or less trustworthy digital format.
2. #### Personal information we DO NOT collect [#personal-information-we-do-not-collect]
The Mobile Wallet is designed to advance privacy and security. We seek to minimise the personal
information we collect in every aspect of the design.
1. Mobile Wallet
The Mobile Wallet is stored on your device only – not on MATTR (or other) servers. This
means that we do NOT collect any person information related to that use – for example we do
not collect location, credential, or log analytic data. That data stays on your device.
We recommend that Mobile Wallet users read the privacy policies of the entities they
interact with through the Mobile Wallet (for example, issuers of credentials).
3. #### Personal Information we DO collect [#personal-information-we-do-collect]
1. Platform
Entities you interact with in the Mobile Wallet may use the Platform. Under the preview
offering, the Platform data is stored on our servers. This means we collect a record of the
transactions that pass through the Platform. Depending on who you interact with in your
Mobile Wallet, this may include credentials related to you. For example, if you are issued a
credential by an entity that uses the Platform to issue it, we may collect that information
in order to provide the Platform functionality, including backup. The information is
encrypted, and we take reasonable steps to secure it. We do not share it outside of MATTR
without user consent, nor do we sell it.
2. Improvements
Our products are designed to increase privacy and security. We are continuously working
towards the end-goal of minimising (and where possible, eliminating) the personal
information we collect on the Platform. The less personal information we collect, the
better! We aim to continue to minimise the amount of personal data we collect, and we will
update you as appropriate.
4. #### How we use and disclose the personal information we collect [#how-we-use-and-disclose-the-personal-information-we-collect]
1. Platform
We use and may sometimes disclose the information we collect to carefully selected and
trusted organisations solely for the purpose of:
1. enabling the product to function as intended, which includes updating, securing, and
troubleshooting, as well as providing support,
2. improving and developing the product,
3. complying with any applicable laws, regulations, industry requirements, court or
authority, or any applicable stock exchange listing rules, and
4. fulfilling any other purpose that you have requested or consented to.
5. We do not use or disclose any information for a purpose not listed above, and we do not
sell your information.
5. #### How you can access your personal information [#how-you-can-access-your-personal-information]
1. Mobile Wallet:
You can access your personal information via the user interface. If you have further
questions or something seems to be missing, you will need to contact entity that issued you
your credential or the entity(s) you provided credentials to as appropriate.
If you delete you Mobile Wallet, all its data will be deleted – including the credentials
stored within it. However, the information may not be deleted by the original issuer or
relying parties. You will need to contact those parties.
2. Platform:
If the Platform stores information about you, you are entitled to access your personal
information (subject to payment of any applicable fees and provided it is readily
retrievable). You may also ask us to confirm whether we hold particular information. These
rights may be subject to certain conditions or grounds for refusal, as set out in the Act.
If you want to exercise any of the above rights, please email us at [privacy@mattr.global](mailto:privacy@mattr.global).
You will need to confirm your identity and set out the details of your request (for example,
the personal information you would like to access, or the correction you are requesting).
6. #### How long we keep your personal information [#how-long-we-keep-your-personal-information]
We will retain your personal information only for as long as is necessary (for the purposes for
which it was collected) and as otherwise required by law.
7. #### Protecting your personal information [#protecting-your-personal-information]
We will take reasonable steps to protect your personal information from loss, disclosure, or
unauthorised use. If a privacy breach occurs, we will communicate with you and all relevant
parties as soon as possible, and report all serious incidents to the Privacy Commissioner. We
will take appropriate steps to minimise the harm of the incident.
8. #### Privacy queries and complaints [#privacy-queries-and-complaints]
If you have any queries about this privacy policy, or would like to make a complaint, you can
contact us at any time at [privacy@mattr.global](mailto:privacy@mattr.global). We will respond
to you as soon as possible.
9. #### Updates [#updates]
We may update the Privacy Policy from time to time by publishing the updated version within the
mobile wallet or on our website. We will update the “Last Updated” date at the top of this page,
and your use after that date confirms your acceptance of the updated policy.
10. #### FAQ [#faq]
##### A. What is personal information? [#a-what-is-personal-information]
Personal information is any data that relates to an individual. When collected together,
otherwise unidentifiable pieces of information can lead to the identification of a particular
person – meaning this also constitutes personal information. Examples of personal information
include name, home address, an email address, or an identification number, such as a driver’s
licence number.
##### B. What is not personal information? [#b-what-is-not-personal-information]
Information that has been irreversibly de-identified (anonymised information) is not personal
information. In addition, personal information does not include public information, such as a
company registration number or company email address (eg. [info@company.com](mailto:info@company.com)).
# MATTR Wallet Terms of Use (archived)
URL: /docs/resources/terms/go-applications/23-9-22
This document is provided for archival purposes only.
Last Updated: 23 September 2022
[See what's changed](/docs/resources/terms/go-applications/recent-changes)
Please note that the English language version of the Terms of Use are binding and the most
up-to-date. If there is any inconsistency between the English language version of the Terms of Use
and any versions in other languages that we provide to you, the English language version will
prevail to the extent of the inconsistency.
1. **OVERVIEW**
These Terms of Use are between MATTR Limited (**MATTR, we**, **us**, or **our**) and you or the
entity you represent (**you** or **your**).
These Terms of Use govern your use of the MATTR Wallet (which includes downloading, installing,
accessing and using) (**use**) on any devices with which the MATTR Wallet is compatible (**Mobile
Device**). Any reference in these Terms of Use to the MATTR Wallet must be read as including a
reference to data, information and content made available through the MATTR Wallet.
By using the MATTR Wallet, you agree to these Terms of Use. If you do not agree, you must uninstall
and not use the MATTR Wallet.
2. **ELIGIBILITY**
You are eligible to use the MATTR Wallet (an **Eligible User**) if you are either:
(a) a user who wishes to test and evaluate the MATTR Wallet; or
(b) a user who has been invited to participate in any MATTR-approved pilot listed on our
website at
[https://learn.mattr.global/docs/resources/terms/go-applications/pilots](https://learn.mattr.global/docs/resources/terms/go-applications/pilots) at
the time of use.
By using the MATTR Wallet, you represent and warrant that you are an Eligible User. If you are not
(or no longer) an Eligible User, you must uninstall and not use the MATTR Wallet.
3. **SCOPE OF LICENCE**
Subject to clauses 2 and 4:
(a) we grant you the revocable, non-exclusive, non-transferable right to use the MATTR Wallet
on any Mobile Device that you own and control. Specifically:
(i) if you are a participant in a MATTR-approved pilot, you can use the MATTR Wallet for the
sole purpose of participating in the pilot, including to provide feedback on the usability of the
MATTR Wallet in the ecosystem to which the pilot relates; and
(ii) otherwise, you can use the MATTR Wallet for the sole purpose of evaluating the capabilities
of the MATTR Wallet and you will not use the MATTR Wallet for commercial purposes unless otherwise
agreed by us in writing.
(b) you understand and agree that:
(i) the MATTR Wallet is not offered as a commercially supported product and is intended to be
used for testing and evaluation purposes only;
(ii) accordingly, as a result of user feedback and/or customer requirements (including any
change to the pilot) and/or to protect MATTR’s legitimate business interests, MATTR may, at any time
and without notice, upgrade, update, replace or discontinue the MATTR Wallet, and/or change the
terms on which the MATTR Wallet is offered (including as to any charges that may apply); and
(iii) if you do not agree with any such changes, your sole remedy is to terminate these Terms of
Use in accordance with clause 10.
(c) You must not distribute the MATTR Wallet or in any way make it available to any third
party, including over a network where it could be used by multiple devices or end users at the same
time. Unless otherwise specified by MATTR, these Terms of Use govern all versions of the MATTR
Wallet, including any upgrades or updates provided or otherwise made available by MATTR.
(d) You must not reverse-engineer, decompile, disassemble, attempt to derive the source code
of, modify, copy or create derivative works of the MATTR Wallet, any updates, or any part of the
MATTR Wallet (except to the extent that any such restriction is prohibited by applicable law).
4. **OPEN SOURCE SOFTWARE**
(a) The MATTR Wallet may include open source software that is subject to one or more open
source licence (**Open Source Software**). Any such Open Source Software is licensed under its
applicable licence terms, and is not subject to the terms and conditions of these Terms of Use
unless otherwise specified. For a full list of Open Source Software used in the MATTR Wallet,
including third-party notices containing credits or attributions where required for the use or
redistribution of such Open Source Software, please visit
[https://learn.mattr.global/docs/holding/go-hold/libraries](https://learn.mattr.global/docs/holding/go-hold/libraries)
(b) Even where Open Source Software is governed by other licence terms, the limitations of
liability set out in clause 8 will continue to apply as between you and MATTR, except to the extent
that any such limitations are prohibited by applicable law.
5. **USE OF DATA**
MATTR may collect, use, disclose and otherwise deal with technical data and related information,
including technical information about your Mobile Device, system and application software, and
peripherals, that is gathered to facilitate the provision of software updates, product support, and
other services related to the MATTR Wallet. MATTR may use this information, as long as it is in a
form that does not personally identify you, to improve our products or to provide services or
technologies to you.
If we collect, use, disclose or otherwise deal with your personal information, we will do so in
accordance with our Privacy Policy, and our Data Processing Terms if applicable, which are available
on our website at [https://learn.mattr.global/docs/resources/terms/privacy-policy](https://learn.mattr.global/docs/resources/terms/privacy-policy)
6. **INTELLECTUAL PROPERTY RIGHTS**
All intellectual property rights in and to the MATTR Wallet, including copyright, are owned by MATTR
or its licensors or assignees. Other than as expressly provided in these Terms of Use, nothing in
these Terms of Use operates to transfer or assign ownership of intellectual property rights, or
confers on you any right, title or interest in or to any of MATTR’s intellectual property rights or
to any third party intellectual property rights.
7. **NO WARRANTY**
Use of the MATTR Wallet is solely at your risk. The MATTR Wallet is provided strictly on an “as is”
and “as available” basis with no express or implied warranty or representation of any kind. MATTR
does not represent or warrant:
(a) the accuracy, merchantability, non-infringement, or fitness of the MATTR Wallet for any
particular purpose;
(b) the accuracy or completeness of any data, information or content (including any text,
graphics, links or other items) contained in or made available through the MATTR Wallet or through
any platforms, including third party platforms, we use to communicate with you in relation to the
MATTR Wallet;
(c) that the MATTR Wallet will be updated or supported by MATTR at any particular frequency
or any particular period of time, or that the MATTR Wallet will support the creation and maintenance
of backups;
(d) that use of the MATTR Wallet will be uninterrupted or error-free; or
(e) that the MATTR Wallet will be free of viruses, worms, denial of service attacks, or other
harmful components or codes.
In some places, like New Zealand and Australia, there may be non-excludable warranties, guarantees
or other rights provided by law. They still apply - these Terms of Use do not exclude, restrict or
modify them. Clause 8 sets out our liability in relation to such non-excludable consumer guarantees.
8. **LIMITATION OF LIABILITY**
To the maximum extent permitted by law, MATTR will not be liable for any direct or indirect
liability, loss, damage, cost or expense (including loss of profits, loss of revenue, business
interruption, loss of savings, loss of information or data, or any indirect, special, incidental or
consequential loss of any kind) arising out of or in connection with your access and use of (or
inability to access and use) the MATTR Wallet, whether in contract, tort (including negligence),
equity, breach of statutory duty or otherwise, even if MATTR has been advised of the possibility of
such damages.
If required under the terms of the store from which you download and install the MATTR Wallet onto
your Mobile Device, MATTR will provide you with a refund of any fees paid for the MATTR Wallet.
Where applicable, any such refund is MATTR’s sole and exclusive liability to you for any liability,
loss, damages, costs and expenses (other than for any liability, loss, damage, costs and expenses
that cannot be limited at law). The refund of fees in any other circumstances is at MATTR’s sole
discretion.
To the extent that the preceding limitations of liability are held to be invalid in whole or in part
in relation to any liability, loss, damages, costs and expenses, then unless the following paragraph
applies, MATTR’s total liability to you for any such liability, loss, damages, costs and expense
(other than to the extent they cannot be limited at law) will not exceed NZD\$50.00.
Nothing in these Terms of Use affects any non-excludable warranties, guarantees or other rights
provided by law as described in clause 7. Our liability for breach of such non-excludable consumer
guarantee is limited, at our option, to re-performing the relevant service (unless the
non-excludable consumer guarantee says otherwise).
9. **THIRD PARTY MATERIALS AND TERMS**
This clause 9 is not to be read as limiting any other provision of these Terms of Use.
The MATTR Wallet may display, or make available certain third party data, information or content
(**Third Party Materials**). You use the Third Party Materials at your own risk. MATTR is not
responsible for examining or evaluating the content or accuracy of any Third Party Materials, and
will not be liable in relation to any Third Party Materials. You must not use the Third Party
Materials in any manner that is inconsistent with these Terms of Use or that infringes the
intellectual property rights of MATTR or any third party.
You are solely responsible for complying with the terms of any applicable third party agreement or
licence terms that apply in connection with such Third Party Materials.
10. **TERMINATION**
These Terms of Use are effective until terminated by you or MATTR.
Your rights under these Terms of Use will terminate automatically if you fail to comply with any of
its terms. If you are a participant in a MATTR-approved pilot, your rights under these Terms of Use
will terminate automatically when the pilot ends, unless otherwise agreed in writing.
If your rights are terminated, you must immediately uninstall and cease to use the MATTR Wallet
(including all copies of the MATTR Wallet).
You may only terminate Terms of Use by uninstalling and ceasing to use the MATTR Wallet (including
all copies of the MATTR Wallet).
Termination of these Terms of Use is without prejudice to the rights and obligations accrued up to
and including the date of termination and either you or we may take action against the other under
these Terms of Use in respect of a breach of Terms of Use arising before the effective date of
termination.
11. **GOVERNING LAW**
These Terms of Use and any dispute or claim arising out of or in connection with it will be governed
by and construed in accordance with the laws of New Zealand and the parties submit to the
non-exclusive jurisdiction of the courts of New Zealand to deal with any dispute, litigation or
other matter relating to these Terms of Use or the MATTR Wallet.
You must not object to the transfer of any proceedings to New Zealand courts on any basis, including
inconvenience. The United Nations Convention on Contracts for the International Sale of Goods does
not apply to this Agreement.
12. **GENERAL**
If, at any time, any provision of these Terms of Use is or becomes illegal, invalid or unenforceable
to any extent, then that provision will be read down so that it becomes legal, valid or enforceable,
or if that is not possible, then that provision will be deleted. The other terms of these Terms of
Use will continue to apply with full force and effect.
Any reference in these Terms of Use to “including” and similar expressions are not used as, nor are
they intended to be, interpreted as words of limitation.
13. **APPLE REQUIREMENTS**
Where you use the MATTR Wallet on an Apple Mobile Device, you acknowledge that:
(a) these Terms of Use are between MATTR and you, and not Apple;
(b) Apple has no responsibility or liability in respect of any matter relating to the MATTR
Wallet, including your use or possession of the MATTR Wallet or the provision of maintenance or
support services relating to the MATTR Wallet;
(c) MATTR, and not Apple, is responsible for addressing any claims relating to the MATTR
Wallet or your possession or use of the MATTR Wallet, including:
(i) product liability claims;
(ii) any claim that the MATTR Wallet fails to conform to any applicable legal or regulatory
requirement; and
(iii) claims arising under consumer protection, privacy or similar legislation;
(d) if the MATTR Wallet fails to conform to any applicable warranty, you may notify Apple, and
Apple’s sole liability will be to refund to you any purchase price for the MATTR Wallet. Apple will
have no other warranty obligation whatsoever with respect to the MATTR Wallet, and any other claims,
losses, liabilities, damages, costs or expenses attributable to any failure to conform to any
warranty not expressly excluded under these Terms of Use will be MATTR’s sole responsibility; and
(e) in the event of any third party claim that the MATTR Wallet, or your possession and use of the
MATTR Wallet, infringes that third party’s intellectual property rights, MATTR (and not Apple) will
be solely responsible for the investigation, defence, settlement and discharge of any such
intellectual property infringement claim.
MATTR and you agree that Apple, and any Apple subsidiary, are third party beneficiaries of these
Terms of Use and that Apple has the right to enforce the Terms of Use against you as a third party
beneficiary. Otherwise, these Terms of Use do not create any third-party beneficiary rights in any
individual or entity that is not a party to these Terms of Use.
You represent and warrant that you are not located in a country that is subject to a US Government
embargo, or that has been designated by the US Government as a terrorist supporting country, and you
are not listed on any US Government list of prohibited or restricted parties.
14. **CONTACT**
If you have any questions, complaints or claims relating to the MATTR Wallet, please contact us at
[privacy@mattr.global](mailto:privacy@mattr.global) or Level 1, Union Fish Building, Britomart
Place, 116-118 Quay Street, Auckland Central, 1010, New Zealand.
## Previous versions [#previous-versions]
* [15 Sep 2020](/docs/resources/terms/go-applications/15-9-20)
# MATTR GO Applications accessibility statement
URL: /docs/resources/terms/go-applications/accessibility-statement
Last Updated: 4 Nov 2024
MATTR is committed to ensuring our products and platforms are accessible to people with
disabilities.
We believe that applications must be inclusive and all MATTR GO Applications are designed to
facilitate an accessible user experience.
We draw upon industry standards and accredited accessibility partners to apply best practices.
## Our accessibility goals [#our-accessibility-goals]
Our aim is to ensure that the wallet is as accessible as possible. We use the industry standard
[Web Content Accessibility Guidelines](https://www.w3.org/TR/WCAG21/) (WCAG 2.1), developed by the
W3C Web Accessibility Initiative.
Our goal is to make MATTR GO Applications experience logical, simple to read and understand and,
hopefully, easier for users to achieve the outcomes they need.
## Our efforts [#our-efforts]
We have dedicated time and resources to make the MATTR GO Applications experiences functional,
engaging and interactive. These are key areas we’ve addressed to assist in accessibility:
* Focus and emphasis on assisted navigation
* Navigation behaviours
* Signposting and labelling
* Colour and contrast
* Touch zones
* Text size
MATTR GO Applications work on both Android and iOS operating systems. Due to the native influence,
there are small, cosmetic differences on each system, however, the fundamental adherence to
accessibility standards has been implemented across each platform.
## Conformance status [#conformance-status]
We are continually improving the user experience for everyone, and will always aim to apply relevant
accessibility standards.
MATTR GO Applications have undergone a number of checks, utilising industry standard and best
practice tooling such as:
* Screen readers
* Screen magnifiers
* Keyboard only checks
* Colour contrast checks
* and a variety of automated and manual checks
MATTR GO Applications are partially conformant with WCAG 2.1 Level AA. Partially conformant means
that some parts of the content do not fully conform to the accessibility standard.
## Give us your feedback [#give-us-your-feedback]
We welcome your feedback on the accessibility of any MATTR GO Applications. Please let us know if
you encounter accessibility barriers on any MATTR GO Application by contacting us at
[privacy@mattr.global](mailto:privacy@mattr.global)
# Approved Pilots for MATTR GO Applications
URL: /docs/resources/terms/go-applications/pilots
This page sets out the MATTR-approved pilots for MATTR GO Applications referred to in clause 2(b) of
the [MATTR GO Applications Terms of Use](/docs/resources/terms/go-applications/terms-of-use). You are eligible
to download, install and use MATTR GO Applications if you have been invited to participate in any of
the following pilots:
* MyCreds™ Ontario Virtual Skills Passport pilot project for micro-credentials
# MATTR GO Applications Terms of Use - What's Changed
URL: /docs/resources/terms/go-applications/recent-changes
MATTR published the MATTR Developer Tool End User Licence Agreement (EULA) on March 25, 2021.
## Changes posted November 5, 2024 [#changes-posted-november-5-2024]
We've renamed the MATTR Wallet Terms of Use into MATTR GO Applications Terms of Use on November
5, 2024.
## Changes posted September 30, 2022 [#changes-posted-september-30-2022]
MATTR updated the EULA / Terms of Use on September 30, 2022.
We’ve renamed the EULA [MATTR Wallet Terms of Use](/docs/resources/terms/go-applications/terms-of-use) and
updated some terms to reflect the changes below:
1. With the Terms of Use being available in multiple languages, we’ve clarified that the English
language version of the Terms of Use is binding and the most up-to-date.
2. As the MATTR Wallet is intended to be used for testing and evaluation purposes only, we’ve
outlined the 2 categories of users who are eligible to download, install and use the app; that
is, individuals who wish to test and evaluate the MATTR Wallet, and participants of approved
pilots listed on our site.
3. We’ve noted that we can change the MATTR Wallet in certain circumstances, such as in response to
user feedback or to protect our business interests. These changes may include replacing or
discontinuing the MATTR Wallet and/or the terms on which it is offered (including any applicable
charges).
By continuing to use the MATTR Wallet after the effective date of these changes, you agree to be
bound by the modified terms.
# MATTR GO Applications Terms of Use
URL: /docs/resources/terms/go-applications/terms-of-use
Last Updated: 5 November 2024
[See what's changed](/docs/resources/terms/go-applications/recent-changes)
Please note that the English language version of the Terms of Use are binding and the most
up-to-date. If there is any inconsistency between the English language version of the Terms of Use
and any versions in other languages that we provide to you, the English language version will
prevail to the extent of the inconsistency.
1. **OVERVIEW**
These Terms of Use (**Terms**) are between MATTR Limited (**MATTR, we**, **us**, or **our**) and you
or the entity you represent (**you** or **your**).
Any download, installation, run, access or other use (**use**) of this application and/or any
application which states it is subject to these Terms (**App**) on any devices with which the App is
compatible (**Device**) is subject to these Terms.
By using the App and/or accepting these Terms, you are agreeing to these Terms. If you do not agree,
you must uninstall and not use the App.
Any reference in these Terms to the App must be read as including a reference to data, information
and content made available through the App.
2. **ELIGIBILITY**
You are eligible to use the App (an **Eligible User**) if you are either:
(a) a user who wishes to test and evaluate the App; or
(b) a user who has been invited to participate in any MATTR-approved pilot listed on our
[website](https://learn.mattr.global/docs/resources/terms/go-applications/pilots) at the time of use.
By using the App, you represent and warrant that you are an Eligible User. If you are not (or no
longer) an Eligible User, you must uninstall and not use the App.
3. **SCOPE OF LICENCE**
Subject to clauses 2, 4 and 5:
(a) we grant you the revocable, non-exclusive, non-transferable right to use the App on any Device
that you own and control. Specifically:
(i) if you are a participant in a MATTR-approved pilot, you can use the App for the sole purpose of
participating in the pilot within the published limitations of that pilot, including to provide
feedback on the usability of the App in the ecosystem to which the pilot relates; and
(ii) otherwise, (A) you can use the App for a non-commercial, non-production evaluation of the
capabilities of the App with the types of credentials, issuers and/or verifies that we notify you
from time to time are authorised for use with the App and; and (B) you will not use the App for
commercial purposes unless otherwise agreed by us in writing.
(b) You must not distribute the App or in any way make it available to any third party, including
over a network where it could be used by multiple devices or end users at the same time. Unless
otherwise specified by MATTR, these Terms govern all versions of the App, including any upgrades or
updates provided or otherwise made available by MATTR.
(c) You must not reverse-engineer, decompile, disassemble, attempt to derive the source code of,
modify, copy or create derivative works of the App, any updates, or any part of the App (except to
the extent that any such restriction is prohibited by applicable law).
4. **TEST PURPOSES ONLY**
You understand and agree that:
(a) the App is not offered as a commercially supported product and is intended to be used for
testing and evaluation purposes only;
(b) accordingly, the App is provided as-is, with no warranties at all (express or implied). We use
reasonable endeavours to avoid it causing material harm to you. Despite this, the App may:
(i) include bugs, errors and security risks;
(ii) perform poorly, produce incorrect results, or cease to function;
(iii) cause your Device, other software or processes on that Device, or other software or processes
with which they interact, to crash or malfunction;
(iv) improperly expose, permanently delete or permanently corrupt, information or data of yours.
(c) to avoid or mitigate against adverse consequences arising from these risks, you must:
(i) limit use of the App to testing purposes only;
(ii) take reasonable precautions in respect of your Device and other software and data on that
Device and not use the App on any Device that is critical for your other business purposes; and
(iii) only enter test or synthetic data into MATTR Services and Product Capabilities under this
Order Form and will ensure that no data you enter is, or is likely to constitute, personal
information or confidential information.
(d) as a result of user feedback and/or customer requirements (including any change to the pilot)
and/or to protect MATTR’s legitimate business interests, MATTR may, at any time and without notice,
upgrade, update, replace or discontinue the App, and/or change the terms on which the App is offered
(including as to any charges that may apply); and if you do not agree with any such changes, your
sole remedy is to terminate these Terms in accordance with clause 11.
5. **OPEN SOURCE SOFTWARE**
(a) The App may include open source software that is subject to one or more open source licence
(**Open Source Software**). Any such Open Source Software is licensed under its applicable licence
terms, and is not subject to the terms and conditions of these Terms unless otherwise specified. For
a full list of Open Source Software used in the App, including third-party notices containing
credits or attributions where required for the use or redistribution of such Open Source Software,
please visit
[https://learn.mattr.global/docs/holding/go-hold/libraries](https://learn.mattr.global/docs/holding/go-hold/libraries) for wallet
or hold Apps and
[https://learn.mattr.global/docs/verification/go-verify/libraries](https://learn.mattr.global/docs/verification/go-verify/libraries) for
verifier Apps.
(b) Even where Open Source Software is governed by other licence terms, the limitations of liability
set out in clause 9 will continue to apply as between you and MATTR, except to the extent that any
such limitations are prohibited by applicable law.
6. **USE OF DATA**
MATTR may collect, use, disclose and otherwise deal with technical data and related information,
including technical information about your Device, system and application software, and peripherals,
that is gathered to facilitate the provision of software updates, product support, and other
services related to the App. MATTR may use this information to improve our products or to provide
services or technologies to you.
If we collect, use, disclose or otherwise deal with your personal information, we will do so in
accordance with our Privacy Policy, and our Data Processing Terms if applicable, which are available
on our website at
[https://learn.mattr.global/docs/resources/terms/privacy-policy](https://learn.mattr.global/docs/resources/terms/privacy-policy).
7. **INTELLECTUAL PROPERTY RIGHTS**
All intellectual property rights in and to the App, including copyright, are owned by MATTR or its
licensors or assignees. Other than as expressly provided in these Terms, nothing in these Terms
operates to transfer or assign ownership of intellectual property rights, or confers on you any
right, title or interest in or to any of MATTR’s intellectual property rights or to any third party
intellectual property rights.
8. **NO WARRANTY**
Use of the App is solely at your risk. The App is provided strictly on an “as is” and “as available”
basis with no express or implied warranty or representation of any kind. MATTR does not represent or
warrant:
(a) the accuracy, merchantability, non-infringement, or fitness of the App for any particular
purpose;
(b) the accuracy or completeness of any data, information or content (including any text, graphics,
links or other items) contained in or made available through the App or through any platforms,
including third party platforms, we use to communicate with you in relation to the App;
(c) that the App will be updated or supported by MATTR at any particular frequency or any particular
period of time, or that the App will support the creation and maintenance of backups;
(d) that use of the App will be uninterrupted or error-free; or
(e) that the App will be free of viruses, worms, denial of service attacks, or other harmful
components or codes.
In some places, like New Zealand and Australia, there may be non-excludable warranties, guarantees
or other rights provided by law. They still apply – these Terms do not exclude, restrict or modify
them. Clause 9 sets out our liability in relation to such non-excludable consumer guarantees.
9. **LIMITATION OF LIABILITY**
To the maximum extent permitted by law, MATTR will not be liable for any direct or indirect
liability, loss, damage, cost or expense (including loss of profits, loss of revenue, business
interruption, loss of savings, loss of information or data, or any indirect, special, incidental or
consequential loss of any kind) arising out of or in connection with your access and use of (or
inability to access and use) the App, whether in contract, tort (including negligence), equity,
breach of statutory duty or otherwise, even if MATTR has been advised of the possibility of such
damages.
If required under the terms of the store from which you download and install the App onto your
Device, MATTR will provide you with a refund of any fees paid for the App. Where applicable, any
such refund is MATTR’s sole and exclusive liability to you for any liability, loss, damages, costs
and expenses (other than for any liability, loss, damage, costs and expenses that cannot be limited
at law). The refund of fees in any other circumstances is at MATTR’s sole discretion.
To the extent that the preceding limitations of liability are held to be invalid in whole or in part
in relation to any liability, loss, damages, costs and expenses, then unless the following paragraph
applies, MATTR’s total liability to you for any such liability, loss, damages, costs and expense
(other than to the extent they cannot be limited at law) will not exceed NZD\$50.00.
Nothing in these Terms affects any non-excludable warranties, guarantees or other rights provided by
law as described in clause 8. Our liability for breach of such non-excludable consumer guarantee is
limited, at our option, to re-performing the relevant service (unless the non-excludable consumer
guarantee says otherwise).
10. **THIRD PARTY MATERIALS AND TERMS**
This clause 10 is not to be read as limiting any other provision of these Terms.
The App may display, or make available certain third party data, information or content (**Third
Party Materials**). You use the Third Party Materials at your own risk. MATTR is not responsible for
examining or evaluating the content or accuracy of any Third Party Materials, and will not be liable
in relation to any Third Party Materials. You must not use the Third Party Materials in any manner
that is inconsistent with these Terms or that infringes the intellectual property rights of MATTR or
any third party.
You are solely responsible for complying with the terms of any applicable third party agreement or
licence terms that apply in connection with such Third Party Materials.
11. **TERMINATION**
These Terms are effective until terminated by you or MATTR.
Your rights under these Terms will terminate automatically if you fail to comply with any of its
terms. If you are a participant in a MATTR-approved pilot, your rights under these Terms will
terminate automatically when the pilot ends, unless otherwise agreed in writing.
If your rights are terminated, you must immediately uninstall and cease to use the App (including
all copies of the App).
You may only terminate these Terms by uninstalling and ceasing to use the App (including all copies
of the App).
Termination of these Terms is without prejudice to the rights and obligations accrued up to and
including the date of termination and either you or we may take action against the other under these
Terms in respect of a breach of Terms arising before the effective date of termination.
12. **GOVERNING LAW**
These Terms and any dispute or claim arising out of or in connection with it will be governed by and
construed in accordance with the laws of New Zealand and the parties submit to the non-exclusive
jurisdiction of the courts of New Zealand to deal with any dispute, litigation or other matter
relating to these Terms or the App.
You must not object to the transfer of any proceedings to New Zealand courts on any basis, including
inconvenience. The United Nations Convention on Contracts for the International Sale of Goods does
not apply to this Agreement.
13. **GENERAL**
If, at any time, any provision of these Terms is or becomes illegal, invalid or unenforceable to any
extent, then that provision will be read down so that it becomes legal, valid or enforceable, or if
that is not possible, then that provision will be deleted. The other terms of these Terms of Use
will continue to apply with full force and effect.
Any reference in these Terms to “including” and similar expressions are not used as, nor are they
intended to be, interpreted as words of limitation.
14. **CHANGES TO THESE TERMS**
We may amend these Terms at any time, by giving you notice of an amended version. By continuing to
use the App after such notice, you agree to the amended Terms. If you do not agree to the amended
terms, you must immediately cease your use of the App.
15. **APPLE REQUIREMENTS**
Where you use the App on an Apple Device, you acknowledge that:
(a) these Terms are between MATTR and you, and not Apple;
(b) Apple has no responsibility or liability in respect of any matter relating to the App, including
your use or possession of the App or the provision of maintenance or support services relating to
the App;
(c) MATTR, and not Apple, is responsible for addressing any claims relating to the App or your
possession or use of the App, including:
(i) product liability claims;
(ii) any claim that the App fails to conform to any applicable legal or regulatory requirement; and
(iii) claims arising under consumer protection, privacy or similar legislation;
(d) if the App fails to conform to any applicable warranty, you may notify Apple, and Apple’s sole
liability will be to refund to you any purchase price for the App. Apple will have no other warranty
obligation whatsoever with respect to the App, and any other claims, losses, liabilities, damages,
costs or expenses attributable to any failure to conform to any warranty not expressly excluded
under these Terms will be MATTR’s sole responsibility; and
(e) in the event of any third party claim that the App, or your possession and use of the App,
infringes that third party’s intellectual property rights, MATTR (and not Apple) will be solely
responsible for the investigation, defence, settlement and discharge of any such intellectual
property infringement claim.
MATTR and you agree that Apple, and any Apple subsidiary, are third party beneficiaries of these
Terms and that Apple has the right to enforce the Terms against you as a third party beneficiary.
Otherwise, these Terms do not create any third-party beneficiary rights in any individual or entity
that is not a party to these Terms.
You represent and warrant that you are not located in a country that is subject to a US Government
embargo, or that has been designated by the US Government as a terrorist supporting country, and you
are not listed on any US Government list of prohibited or restricted parties.
16. **CONTACT**
If you have any questions, complaints or claims relating to the App, please contact us at
[privacy@mattr.global](mailto:privacy@mattr.global).
## Previous versions [#previous-versions]
* [23 Sep 2022](/docs/resources/terms/go-applications/23-9-22)
* [15 Sep 2020](/docs/resources/terms/go-applications/15-9-20)
# MATTR Privacy Policy (archived)
URL: /docs/resources/terms/privacy-policy/10-11-22
This document is provided for archival purposes only.
Last Updated: 10 November 2022
[See what's changed](/docs/resources/terms/privacy-policy/recent-changes) |
[Previous versions](#previous-versions)
1. #### Your Privacy Matters [#your-privacy-matters]
MATTR is built and operated as a privacy-first company. Enhancement of privacy and trust in
digital transactions are fundamental to the software we develop. We aim to support entities
participating in the growing eco-system of privacy-preserving verifiable data transactions. We
do not sell, rent or otherwise monetise personal information that we process on our customers’
behalf.
This Privacy Policy sets out how we collect, use, disclose and protect personal information
when:
* we provide Services to our customers through our software-as-a-service platform (e.g., when we
issue or verify credentials)
* we provide other Services to our customers (such as training or seminars), or
* we provide access to or use of our Materials, such as apps, SDKs, and the content, materials,
software, data, documents (etc.) we make available to allow use of our Services.
(For a full definition of all of our Services and Materials, see our
[Customer Agreement](https://learn.mattr.global/docs/resources/terms/customer-agreement), which may be
updated from time to time).
First and foremost, we use your personal information to provide our customers with requested
Services and Materials and to manage our relationship with you and our customers. Where end
users’ personal information is provided to us for the purpose of us providing our Services (e.g.
when our customer issues a credential using our Services), we use and disclose that end user
personal information for service delivery and the other limited purposes set out in this Privacy
Policy. We do not use it for purposes like marketing services from us or others to the end user.
If you access or use our website, sign-up to receive MATTR communications or content, or
otherwise use Materials without representing or becoming our customer, the
[MATTR Website Privacy Policy](https://learn.mattr.global/docs/resources/terms/website-privacy-policy/9-12-21)
applies.
If you don’t have a relationship with us, but believe your personal information is used by an
entity that accesses or uses our Materials or Services, that entity’s privacy policy applies to
their collection, use and disclosure of your personal information. In the first instance, we
recommend that you contact that entity for any questions you have about your personal
information (including where you want to access, correct, amend, or request the deletion of,
your personal information).
2. #### Who we are [#who-we-are]
1. When we say “**our**”, “**we**”, or “**us**”, we mean MATTR Limited (**MATTR**). Our offices
are in New Zealand, but we operate globally. We provide easy-to use Services and Materials to
improve trust and privacy in digital interactions.
2. We collect, use and share personal information in accordance with applicable law such as the
Privacy Act 2020 (NZ) (**Privacy Act**).
3. If a customer accesses or uses our Services or Materials in the European Economic Area, the
United Kingdom (**EEA**), Switzerland or in relation to any natural person who is identified
or identifiable and in the EEA or Switzerland, the
[MATTR Data Processing Terms](https://learn.mattr.global/docs/resources/terms/data-processing-terms)
apply to how we process that personal data.
4. For clarity, when we refer to "you" or "your" we mean an individual whose personal
information is processed using our Services or who accesses the Materials on behalf of a
customer.
3. #### Our principles of data protection [#our-principles-of-data-protection]
1. Our approach to privacy and data protection is built around four key principles. They’re at
the heart of everything we do relating to personal information.
1. Transparency: We take a human approach to how we process personal information by being
open, honest and transparent.
2. Security: We champion industry leading approaches to securing the personal information
entrusted to us.
3. Stewardship: We accept the responsibility that comes with processing personal
information.
4. Data minimisation: We are continuously working to minimise the personal information that
we collect and develop more privacy preserving features.
4. #### Types of personal data we collect [#types-of-personal-data-we-collect]
1. When we say “personal information” we mean identifiable information about you that we collect
when our customers access or use our Services or Materials. Examples of personal information
include name, email address, phone number, bank account details, identifiable support queries
and community comments, and so on.
2. You or our customers may disclose some of this personal information to us optionally, or we
may need it to provide the Services (for example, payment information).
3. If you can’t be identified (for example, when personal information has been aggregated and
anonymised) then this Privacy Policy doesn’t apply.
5. #### How we collect personal information [#how-we-collect-personal-information]
1. We collect personal information that we need to provide our customers with our Services or
Materials, and any information you provide to us optionally. The way we collect this personal
information can be broadly categorised into the following:
1. **Information you provide to us directly:** For example, if you use the Sign-Up feature
on our website to get access to our Services for an organisation you represent (including
during a trial period or on a preview basis), we may ask you for your name, organisation,
payment information, and email so that we can correctly assess your application and
discuss your requirements (if applicable). You may also provide us with similar contact
information if you contact us for support, participate in community forums, join us on
social media, or take part in training and events. If you don’t want to provide us with
such personal information, it may mean that we cannot provide you with certain Services
or Materials.
2. **Information we collect automatically:** We may collect some personal information
automatically when our customer accesses or uses our Services and Materials, including
through the use of cookies. This can include IP addresses, interactions with our
Services, and the user and customer accounts with which interactions are linked. This
personal information helps us to operate and provide our Services and Materials, get a
better understanding of how our Services and Materials are accessed and used and may be
used to improve our Services and Materials. Note that if you’re just using or accessing
our website, the
[Website Privacy Policy](https://learn.mattr.global/docs/resources/terms/data-processing-terms)
applies.
3. **Information we collect from third parties:** We usually collect personal information
directly from you or from our customer. Our customer may be an agency that you have a
direct relationship with (like an educational institution or government department) or
its service provider. Sometimes we collect personal information about you from other
sources, such as publicly available materials or trusted third parties (such as research
partners or the issuer of a credential that you hold and that our Service is being used
to verify). We may also collect and use this personal information to better inform,
personalise and improve our Services.
6. #### How we use your personal information [#how-we-use-your-personal-information]
1. First and foremost, we use your personal information to provide our customers with requested
Services and Materials and to manage our relationship with you and our customers. Where end
users’ personal information is provided to us for the purpose of us providing our Services
(e.g. when our customer issues a credential using our Services), we use and disclose that end
user personal information for service delivery and the other limited purposes set out in this
Privacy Policy. We do not use it for purposes like marketing services from us or others to
the end user. We may also use your personal information to:
2. **Communicate with you**. This may include:
1. providing you with information you’ve requested from us (like training or education
materials) or information we are required to send to you;
2. operational communications, like changes to our Services, security updates, or assistance
with using our Services;
3. marketing communications (about us or another product or Service we think you might be
interested in) if you have opted in with your marketing preferences (including by
tracking your use of, and interaction with, our website and marketing emails); or
4. asking you for feedback or to take part in any research we are conducting (which we may
engage a third party to assist with).
3. **Support you**: This may include assisting with the resolution of technical support issues
or other issues relating our Services, whether by email, in-app support or otherwise.
4. **Enhance our Services and develop new ones**: Such as providing new or improved tools and
optimising user experiences.
5. **Protect our Services and Materials**: So that we can detect and prevent any fraudulent or
malicious activity, and make sure that everyone is using our Services fairly and in
accordance with any applicable terms and conditions.
6. **Comply with legal requirements**: To comply with applicable laws, regulations or legal
processes, demonstrate such compliance, or to exercise, establish or defend our legal rights.
7. #### How we minimise the sharing of your personal information [#how-we-minimise-the-sharing-of-your-personal-information]
1. Where we collect personal information, we’ll only disclose it as reasonably required:
1. to provide our customers with our Services and Materials, including to other MATTR group
companies and trusted partners and service providers who are involved in the provision of
the Services or Materials or are otherwise providing goods or services to MATTR and have
agreed to protect your personal information in a manner consistent with this Privacy
Policy
2. to the extent that such disclosure is necessary for us to use the personal information
for the purpose it was collected
3. in accordance with this Privacy Policy and any other applicable data protection and
privacy laws (including as set out in the MATTR Customer Agreement and any applicable
Service Terms and Service Level Agreement)
4. to regulators, law enforcement bodies, government agencies, courts or other third parties
if required to comply with applicable laws, regulations or legal processes, demonstrate
such compliance, or to exercise, establish or defend our legal rights (but we’ll try to
notify you about these kinds of disclosures if possible)
5. to prevent, detect, or investigate security concerns, including fraud
6. where you are a customer representative, to an actual or potential buyer (and its agents
and advisors) in connection with an actual or proposed purchase, merger or acquisition of
any part of our business, or
7. with your consent.
8. #### International Transfers of your personal information [#international-transfers-of-your-personal-information]
1. When we do share your personal information, it may be transferred to, and processed in, a
country different to where you a located. These countries may have laws that are different to
what you are accustomed to. Where this is the case, we put comparable safeguards in place to
ensure your personal information remains protected.
2. For individuals in the European Economic Area (EEA), Switzerland or in relation to any
natural person who is identified or identifiable and in the EEA or Switzerland, this means
that your data may be transferred outside of the EEA or Switzerland in accordance with the
MATTR Data Processing Terms. For further information, please contact us using the details set
out in the “How to contact us” section below.
9. #### Security [#security]
1. Security is a priority for us when it comes to your personal information. We’re committed to
protecting your personal information and have appropriate technical, physical and
organisational measures in place to protect your personal information. For more information
about the security of your personal information, you can contact us.
10. #### Retention [#retention]
1. The length of time we keep your personal information depends on the type of personal
information and whether we have an ongoing business need to retain it (for example, to
provide a requested Service to our customer or to comply with applicable legal or tax
requirements). We’ll retain personal information only for as long as is necessary.
2. Personal information collected by MATTR may be stored and processed in the region in which it
is collected and in any other region where we maintain operations, including New Zealand and
the EEA.
11. #### Your rights [#your-rights]
1. It’s your personal information and you have rights, including to:
1. know what personal information we hold about you;
2. access the personal information we hold about you; and
3. request a correction of the personal information.
2. You can exercise these rights, and any other rights you may have under applicable data
protection and privacy laws, at any time by making a request to us. If you’re not happy with
how we are collecting, using or disclosing your personal information, please let us know by
contacting us. Your requests may be subject to certain conditions or grounds for refusal, as
set out under applicable data protection and privacy laws. We will review and investigate
your complaint and try to get back to you within a reasonable timeframe. You can also request
investigation by the privacy regulator with jurisdiction for your matter (e.g. New Zealand
Privacy Commissioner) at any time during or after raising a complaint with us.
12. #### Changes [#changes]
1. We may need to update this Privacy Policy from time to time. We will publish the updated
version on our website. Where a change is significant, we’ll also endeavour to let our
customers know by email. Any such changes will come into effect 30 days after the updated
version is published.
13. #### How to contact us [#how-to-contact-us]
1. We’re always keen to hear from you. If you’re curious about what personal information we hold
about you or have a question or feedback for us on this Privacy Policy, please contact our
Privacy and Data Protection Officer at [privacy@mattr.global](mailto:privacy@mattr.global).
2. As a technology company, we prefer to communicate with you by email – this ensures that
you’re put in contact with the right person, in the right location, and in accordance with
any regulatory timeframes.
## Previous versions [#previous-versions]
* [MATTR Privacy Policy - 9 December 2021 (archived)](/docs/resources/terms/privacy-policy/9-12-21)
* [MATTR Privacy Policy - 25 March 2021 (archived)](/docs/resources/terms/privacy-policy/25-3-21)
# MATTR Privacy Policy (archived)
URL: /docs/resources/terms/privacy-policy/17-10-25
This document is provided for archival purposes only.
Last Updated: 17 October 2025
[See what's changed](/docs/resources/terms/privacy-policy/recent-changes) |
[Previous versions](#previous-versions)
1. #### Your Privacy Matters [#your-privacy-matters]
1. MATTR is built and operated as a privacy-first company. Enhancement of privacy and trust in
digital transactions are fundamental to the software we develop. We aim to support entities
participating in the growing eco-system of privacy-preserving verifiable data transactions.
2. This Privacy Policy sets out how we collect, use, disclose and protect personal information
which we process on our own behalf (that is, as a data controller). It does not cover
situations where we process personal information on behalf of another party, or as their
agent or data processor (such as where a customer uses our Services or Materials to issue,
verify or otherwise manage credentials). In these circumstances the other entity (e.g. the
customer issuing, verifying or managing the credentials using our Services or Materials) has
its own privacy policy which will apply instead of this Privacy Policy. In the first
instance, we recommend that you contact that entity for any questions you have about your
personal information (including where you want to access, correct, amend, or request the
deletion of, your personal information).
2. #### Who we are [#who-we-are]
1. When we say “our”, “we”, or “us”, we mean MATTR Limited a New Zealand company, NZBN
9429047578432 (MATTR). Our corporate headquarters are in New Zealand, but we operate globally
and have group companies and personnel in a number of other locations. We provide easy-to use
Services and Materials to improve trust and privacy in digital interactions.
2. We carefully comply with all privacy and data protection laws applicable to our business. The
specific privacy laws that apply to our processing of your information will depend on where
you are located.
3. For clarity, when we refer to "you" or "your" we mean an individual whose personal
information is processed by us.
3. #### Our principles of data protection [#our-principles-of-data-protection]
1. **General Supply commitments:** Our approach to privacy and data protection is built around
four key principles. They’re at the heart of everything we do relating to personal
information.
1. Transparency: We take a human approach to how we process personal information by being
open, honest and transparent.
2. Security: We champion industry leading approaches to securing the personal information
entrusted to us.
3. Stewardship: We accept the responsibility that comes with processing personal
information.
4. Data minimisation: We are continuously working to minimise the personal information that
we collect and develop more privacy preserving features.
4. #### Types of personal data we collect [#types-of-personal-data-we-collect]
1. When we say “personal information” we mean identifiable information about you. If you can’t
be identified (for example, when personal information has been aggregated and anonymised)
then this Privacy Policy doesn’t apply.
2. We collect personal information when you provide it to us, when you interact with our
products and services, websites and electronic systems, when you attend events and visit our
offices, and when other sources provide it to us, as further described below:
1. **Directly provided information:** We collect personal information that you provide to us
directly. For example, if you fill out a form or sign up to our Services on our website,
we will collect any information you provide to us in that context and as you use the
Services you may input personal information in order to manage and configure your
account. You may also provide us personal information if you contact us for support,
participate in community forums, join us on social media, take part in training and
events or visit our offices. If you don’t want to provide us with such personal
information, it may mean that we cannot provide you with certain Services or Materials.
2. **Automatically collected information when using our website:** When using our website,
we collect analytics data about how you use and interact with our site, which may include
your online device information, such as the IP address and device ID of the device you
are using, the domain name from which you are accessing the internet, the operating
system and the browser your device uses, any search engine you are using, your browsing
behaviour and click activities, date and time you are visiting, location data based on
where your browsing session originates, urls of pages you visit, the domain name of pages
you visit directly before or after coming to our site, login history, performance results
for the website, details of any errors or system crashes/failures (“**Usage Data**”). We
have set out specific information about the cookies we use on our websites below at
section 5 (Our website, cookies and similar technologies).
3. **Automatically collected information when using Services:** We collect information about
the way you use or interact with our Services and Materials and actions taken via your
account, including Usage Data associated with that use. We combine that with other
personal information we hold about you (for example, your user and customer accounts with
which interactions are linked). In addition, we may collect data about any problems you
experience with our websites, applications, Services or Materials, including bug, error,
and crash reports, which can include Usage Data such as device information, location, and
user data at the time of the bug, error, and/or crash.
4. **Information collected from third parties:** We sometimes receive personal information
about you from third parties. We have summarised some of the ways we might do that below:
1. We may receive business contact information (such as name, job title, business email,
phone number, and address), social profile (such as LinkedIn) including other details
about your organization for sales and marketing purposes, to better inform you about
MATTR products and services. Typically, and subject to applicable laws, we might
receive this information from: (A) third-party marketing initiatives, such as events
where we are a sponsor, or website forms hosted by third parties that may provide
content about us; (B) where you consent to having your attendee badge scanned at an
event hosted by us or another entity; (C) companies, such as information aggregators
and similar entities, from whom we have licensed business contact information; (D)
publicly available sources, such as your social media profile, articles or interviews
you have previously given; (E) referrals; or (F) resellers and channel partners,
including those that offer joint marketing services. In some situations, we may
combine such business contact information with other non-personal and personal
information we possess or that you have provided to us for sales and marketing
purposes.
2. We may receive information from third-party platforms for various business purposes
such as research and development, organizational credit information, program
management, or technical reasons. For example, we may receive credit information
about an organization that includes the names of individuals.
3. If you are a candidate applying for a job at MATTR, subject to laws applicable in
your jurisdiction, we may receive personal information about you from third parties
for business purposes, such as through background checks (educational, employment,
criminal, and financial information), publicly-available sources (like social media
accounts, including LinkedIn for identifying candidates), feedback about your
application and from interviews, and other third parties that may provide feedback
about your application. MATTR treats references provided as part of a job application
as confidential.
5. #### Our website, cookies and similar technologies [#our-website-cookies-and-similar-technologies]
1. A cookie is a small text file that websites can ask your browser to store on your device to
enhance your experience and collect and understand Usage Data. You can disable cookies by
changing the settings on your browser, by not providing consent for us to use cookies, or by
changing your cookie preferences, but this may impact your experience of our website. We
summarise some of the cookies we use on our website below.
***Google Ads***
2. We use the Google Ads platform to advertise and to understand the performance of our
advertising for marketing and sales purposes (ad conversion data). To enable this, if you
provide consent, we use cookies which may store information including:
1. a unique identifier for you (as a visitor of our website); and
2. a particular advertisement click that brought you to our website
3. whether you take particular actions after clicking through from an advertisement, such as
submitting a sales inquiry.
3. This information is shared with Google for them to provide us with ad conversion data. If you
are otherwise identifiable by Google, for example because you are signed into a Google
application or service, you may be identifiable to them and us in connection with ad
conversion data related to you. You can read more about how Google uses information from
websites like us that use Google’s services
[here](https://policies.google.com/technologies/partner-sites).
4. You can opt-out by visiting the Google Ad Setting website
[here](https://adssettings.google.com/authenticated). To learn more about Google’s privacy
practices you can read Google’s Privacy Policy and Terms website
[here](https://policies.google.com/privacy).
***Preventing bots and spam***
5. Our website is protected by the hCaptcha anti-bot service, which checks whether the data
entered on our website has been entered by a human or by an automated program. To learn
more, you can read hCaptcha’s [privacy policy](https://www.hcaptcha.com/privacy) and
[terms of service](https://www.hcaptcha.com/terms).
***Privacy-focused analytics***
6. In addition to Google Ads, which is described in the Cookies section above, we’ve
made the conscious choice to pay for a privacy-first analytics tool called Matomo. To learn
more, you can read about [privacy-focused analytics](https://matomo.org/privacy/) and
Matomo’s [privacy policy](https://matomo.org/matomo-cloud-privacy-policy/).
6. #### How we use your personal information [#how-we-use-your-personal-information]
1. We have summarised below the ways MATTR uses personal information:
1. **Service delivery:** First and foremost, we use your personal information to provide our
customer and/or you with Services and Materials and to manage our relationship with you
and our customers.
2. **Communicate with you:** We may communicate with you in respect of the following (if you
wish to cease being the point of contact for one of our customers please let us know):
1. providing you with information you’ve requested from us (like training or education
materials) or other information we need to send to you;
2. respond to you if you make an enquiry or apply for a job with us;
3. sending you operational or service communications, like changes to our Services,
release notifications, incident or security updates, assistance with using our
Services or tailored communications based on your activity and interactions with us;
4. asking you for feedback or to take part in any research we are conducting (which we
may engage a third party to assist with).
3. **Support:** We may provide technical support to customer representatives or individual
users of our customers. This may include assisting with the resolution of technical
support issues or other issues relating our Services, whether by email, in-app support or
otherwise.
4. **Enhance and improve:** We analyse and predict how our existing website, marketing
campaigns, Services and Materials are used, develop and test new or improved features,
tools, Services or Materials, marketing campaigns or promotions, optimise user
experiences and make them more personalised to you or groups of users like you.
5. **Fraud prevention and system protection:** We monitor use of our website, Services and
Materials for the purpose of detecting and preventing fraudulent or malicious activity,
and to make sure that everyone is using our Services fairly and in accordance with any
applicable terms and conditions.
6. **Business development:** We use personal information (including business contact data
and Usage Data in respect of our websites) for business development purposes. This may
include identifying the organisation you work for when browsing our website, assessing
the likelihood that your organisation may purchase from us, identifying business
opportunities, building a profile of categories of individuals or organizations likely to
be interested in our products, and introducing members of our sales team to you, sending
you tailored content, and recording our business development interactions or
communications with you and other details we have learned about you in the course of
those interactions.
7. **Marketing:** Where we have your consent and/or otherwise in accordance with applicable
law, we may use personal information (including contact data, Usage Data in respect of
(i) our website and/or (ii) our Services and Materials) to send promotional
communications that may be of interest to you and your organization, including by email
and by displaying MATTR marketing communications on other organizations’ websites and
applications, as well as on third-party platforms like Facebook, X, and Google subject to
laws applicable in your jurisdiction. These communications are aimed at encouraging
engagement and maximizing the benefits that you and your organization can gain from
MATTR’s products and services, including information about new products and features,
survey requests, newsletters, and events that we think may be of interest to you and your
organization. We may also use personal information to analyse these communication
efforts, build target audiences to ensure these communications are more effective and
relevant to each recipient. You can opt out of marketing emails at any time, by clicking
on the unsubscribe link and/or contacting us at [privacy@mattr.global](mailto:privacy@mattr.global).
8. **Recruiting and hiring:** We process personal information, such as contact details, job
applicant, and biographical data, to assess job applications and to evaluate and improve
our recruitment system, our application tracking and recruitment activities. We may also:
(i) communicate with you regarding your application or opportunities at MATTR that appear
over time that we believe may be of interest to you; and/or (ii) send you new hire and
employee experience information. We may verify your information, including through
confidential reference checks and, to the extent permitted in accordance with applicable
law, carry out background checks.
9. **Comply with legal requirements:** We process personal information to comply with
applicable laws, regulations or legal processes, demonstrate such compliance, or to
exercise, establish or defend our legal rights, respond to legal claims and undertake
compliance programmes.
10. **Other purposes in our legitimate interests:** We process personal information for
other purposes not expressly set out above where it is within the legitimate interests
of our business operations and management, including but not limited to, for operational
purposes and workflow automation, business intelligence, regulatory, and audit
functions, protecting personal property or safety.
7. #### Disclosure of your personal information [#disclosure-of-your-personal-information]
1. We may disclose personal information in the circumstances set out below.
1. **Intended purpose.** We may disclose personal information to third parties where
required/requested by you as part of providing the Services and Materials or is otherwise
necessary for us to handle the personal information for the purpose it was collected.
2. **Service providers.** We may disclose all categories of personal information to our
suppliers and service providers for various business purposes, including, but not limited
to, auditing interactions with users, debugging our websites, products and services,
security purposes, internal research and gleaning insights through machine learning,
short-term uses such as credit verification, payment processing, IT services, quality
control and safety, in-person and virtual event management, as well as to perform other
services on our behalf. We also use a range of service providers and suppliers who
process personal information on our behalf as our agent or as a data processor, including
the following: [https://learn.mattr.global/docs/resources/terms/data-sub-processors](https://learn.mattr.global/docs/resources/terms/data-sub-processors).
3. **Affiliates and professional advisers.** We disclose personal information to our
affiliates and subsidiaries for business purposes. For example to our group companies as
part of providing the Services or Materials or supporting our back office functions. We
will also disclose your personal information to our professional service providers (for
example, our auditors, insurance providers, financial service providers, and legal
advisors) as needed for us to run our business.
4. **Advertising and marketing.** In some circumstances we share personal information, such
as Usage Data and/or contact data, with third-party advertising and marketing providers,
to allow us to better reach our customers and prospective customers, and to sell our
products and services, to the extent permitted by laws applicable in your jurisdiction.
In some circumstances, we may ask you to consent to directly disclosing your personal
information with these third parties prior to sharing your personal information, such as
via a consent banner on our website.
5. **Job applications.** When you apply for a job at MATTR, we disclose your personal
information, including applicant data, biographical information, and other personal
information we possess to our affiliate companies for business reasons, such as human
resource management and internal reporting; our service providers for business reasons,
such as the recruitment platform, to verify references and to manage background checks;
and law enforcement or government authorities, or as otherwise necessary to comply with
law or as needed for the recruitment and human resources process.
6. **Law, legal process and fraud prevention.** We may disclose personal information:
1. as otherwise set out in this Privacy Policy or any other agreements or documentation
that apply to our processing of your personal information (including the MATTR
Customer Agreement, any applicable Service Terms and the Service Level Agreement);
2. to regulators, law enforcement bodies, government agencies, courts or other third
parties if required to comply with applicable laws, regulations or legal processes,
demonstrate such compliance, or to exercise, establish or defend our legal rights
(but we’ll try to notify you about these kinds of disclosures if possible);
3. to prevent, detect, investigate or mitigate security concerns, including fraud;
4. as required for us to carry out a corporate transaction, such as a merger or sale of
assets of all or part of our company (this may include, where you are a customer
representative, disclosing your personal information to an actual or potential buyer
and its agents and advisors); or
5. otherwise with your consent.
8. #### International Transfers of your personal information [#international-transfers-of-your-personal-information]
1. When we do disclose your personal information as set out above, it may be transferred to, and
processed in, a country different to where you are located. These countries may have laws
that are different to what you are accustomed to. Where this is the case, we put in place
appropriate safeguards as required by applicable law to ensure your personal information
remains protected.
2. We may also transfer your personal information to our group companies in other parts of the
world in the context of using it in accordance with this policy (in particular to our
corporate headquarters in New Zealand, where many of our personnel and back office functions
are located).
3. For individuals in the European Economic Area (EEA) or Switzerland this means that your data
may be transferred outside of the EEA or Switzerland. For further information, please contact
us using the details set out in the “How to contact us” section below.
9. #### Security [#security]
1. Security is a priority for us when it comes to your personal information. We maintain an
information security programme (including the adoption and enforcement of internal policies
and procedures) designed to protect personal information against misuse, identify reasonably
foreseeable and internal security risks and minimise security risks. For more information
about the security of your personal information, you can contact us.
10. #### Retention [#retention]
1. The length of time we keep your personal information depends on the type of personal
information and whether we have an ongoing business need to retain it (for example, to
provide a requested Service to our customer or to comply with applicable legal or tax
requirements). We’ll retain personal information only for as long as is necessary.
11. #### Your rights [#your-rights]
1. Depending on where you are located and what privacy laws apply, you have a number of rights
with respect to our use of your personal information.
2. These may include a right to access or correct your personal information, opt out of
marketing emails or other services or object to any processing of your personal information.
If you are in the European Union, you have additional rights, which we will ensure we comply
with.
3. You can exercise these rights, and any other rights you may have under applicable data
protection and privacy laws, at any time by making a request to us via email to
[privacy@mattr.global](mailto:privacy@mattr.global).
4. If you’re not happy with how we are collecting, using or disclosing your personal
information, please let us know by contacting us. Your requests may be subject to certain
conditions or grounds for refusal, as set out under applicable data protection and privacy
laws. We will review and investigate your complaint and try to get back to you within a
reasonable timeframe.
12. #### Changes [#changes]
1. We will update this Privacy Policy from time to time. When we do so we will publish the
updated version on our website.
13. #### How to contact us [#how-to-contact-us]
1. We’re always keen to hear from you. If you’re curious about what personal information we hold
about you or have a question or feedback for us on this Privacy Policy, please contact our
Privacy and Data Protection Officer at [privacy@mattr.global](mailto:privacy@mattr.global).
## Previous versions [#previous-versions]
### Privacy policy [#privacy-policy]
* [MATTR Privacy Policy - 5 November 2024 (archived)](/docs/resources/terms/privacy-policy/5-11-24)
* [MATTR Privacy Policy - 10 November 2022 (archived)](/docs/resources/terms/privacy-policy/10-11-22)
* [MATTR Privacy Policy - 9 December 2021 (archived)](/docs/resources/terms/privacy-policy/9-12-21)
* [MATTR Privacy Policy - 25 March 2021 (archived)](/docs/resources/terms/privacy-policy/25-3-21)
### Website privacy policy [#website-privacy-policy]
* [MATTR Website Privacy Policy - 9 December 2021 (archived)](/docs/resources/terms/website-privacy-policy/9-12-21)
* [MATTR Website Privacy Policy - 20 July 2021 (archived)](/docs/resources/terms/website-privacy-policy/20-7-21)
* [MATTR Website Privacy Policy - 25 March 2021 (archived)](/docs/resources/terms/website-privacy-policy/25-3-21)
# MATTR Privacy Policy (archived)
URL: /docs/resources/terms/privacy-policy/25-3-21
This document is provided for archival purposes only.
Last Updated: 25 March 2021
## Your Privacy Matters [#your-privacy-matters]
MATTR is built and operated as a privacy-first company. Enhancement of privacy and trust in digital
transactions are fundamental to the software we develop. We aim to support entities participating in
the growing eco-system of privacy-preserving verifiable data transactions. We do not sell, rent or
otherwise monetise personal information that we process on our customers’ behalf.
This privacy policy (**Privacy Policy**) sets out how we collect, use, disclose and protect personal
information when:
* we provide Services to our customers through our software-as-a-service platform (e.g., when we
issue or verify credentials)
* we provide other Services to our customers (such as training or seminars), or
* we provide access to or use of our Materials, such as apps, SDKs, and the content, materials,
software, data, documents (etc.) we make available to allow use of our Services.
(For a full definition of all of our Services and Materials, see our
[Customer Agreement](/docs/resources/terms/customer-agreement), which may be updated from time to time).
First and foremost, we use your personal information to provide our customers with requested
Services and Materials and to manage our relationship with you and our customers. Where end users’
personal information is provided to us for the purpose of us providing our Services (e.g. when our
customer issues a credential using our Services), we use and disclose that end user personal
information for service delivery and the other limited purposes set out in this Privacy Policy. We
do not use it for purposes like marketing services from us or others to the end user.
If you access or use our website or otherwise use Materials without representing or becoming our
customer, the [MATTR Website Privacy Policy](/docs/resources/terms/website-privacy-policy/25-3-21) applies.
If you don’t have a relationship with us, but believe your personal information is used by an entity
that accesses or uses our Materials or Services, that entity’s privacy policy applies to their
collection, use and disclosure of your personal information. In the first instance, we recommend
that you contact that entity for any questions you have about your personal information (including
where you want to access, correct, amend, or request the deletion of, your personal information).
1. #### Who we are [#who-we-are]
1. When we say “**our**”, “**we**”, or “**us**”, we mean MATTR Limited (**MATTR**). Our offices
are in New Zealand, but we operate globally. We provide easy-to use Services and Materials to
improve trust and privacy in digital interactions.
2. We collect, use and share personal information in accordance with the Privacy Act 2020 (NZ)
(**Privacy Act**).
3. If a customer accesses or uses our Services or Materials in the European Economic Area or the
United Kingdom (**EEA**), or in relation to any natural person who is identified or
identifiable and in the EEA, the
[MATTR Data Processing Terms](/docs/resources/terms/data-processing-terms) apply to how we process that
personal data.
4. For clarity, when we refer to "**you**" or "**your**" we mean an individual whose personal
information is processed using our Services or who accesses the Materials on behalf of a
customer.
2. #### Our principles of data protection [#our-principles-of-data-protection]
1. Our approach to privacy and data protection is built around four key principles. They’re at
the heart of everything we do relating to personal information.
1. **Transparency**: We take a human approach to how we process personal information by
being open, honest and transparent.
2. **Security**: We champion industry leading approaches to securing the personal
information entrusted to us.
3. **Stewardship**: We accept the responsibility that comes with processing personal
information.
4. **Data minimisation**: We are continuously working to minimise the personal information
that we collect and develop more privacy preserving features.
3. #### Types of personal data we collect [#types-of-personal-data-we-collect]
1. When we say “**personal information**” we mean identifiable information about you that we
collect when our customers access or use our Services or Materials. Examples of personal
information include name, email address, phone number, bank account details, identifiable
support queries and community comments, and so on.
2. You or our customers may disclose some of this personal information to us optionally, or we
may need it to provide the Services (for example, payment information).
3. If you can’t be identified (for example, when personal information has been aggregated and
anonymised) then this Privacy Policy doesn’t apply.
4. #### How we collect personal information [#how-we-collect-personal-information]
1. We collect personal information that we need to provide our customers with our Services or
Materials, and any information you provide to us optionally. The way we collect this personal
information can be broadly categorised into the following:
1. **Information you provide to us directly**: For example, if you use the Sign-Up feature
on our website to get access to our Services for an organisation you represent (including
during a trial period or on a preview basis), we may ask you for your name, organisation,
payment information, and email so that we can correctly assess your application and
discuss your requirements (if applicable). You may also provide us with similar contact
information if you contact us for support, participate in community forums, join us on
social media, or take part in training and events. If you don’t want to provide us with
such personal information, it may mean that we cannot provide you with certain Services
or Materials.
2. **Information we collect automatically**: We may collect some personal information
automatically when our customer accesses or uses our Services and Materials, including
through the use of cookies. This can include IP addresses, interactions with our
Services, and the user and customer accounts with which interactions are linked. This
personal information helps us to operate and provide our Services and Materials, get a
better understanding of how our Services and Materials are accessed and used and may be
used to improve our Services and Materials. Note that if you’re just using or accessing
our website, the [Website Privacy Policy](/docs/resources/terms/website-privacy-policy/25-3-21) applies.
3. **Information we collect from third parties**: We usually collect personal information
directly from you or from our customer. Our customer may be an agency that you have a
direct relationship with (like an educational institution or government department) or
its service provider. Sometimes we collect personal information about you from other
sources, such as publicly available materials or trusted third parties (such as research
partners or the issuer of a credential that you hold and that our Service is being used
to verify). We may also collect and use this personal information to better inform,
personalise and improve our Services.
5. #### How we use your personal information [#how-we-use-your-personal-information]
1. First and foremost, we use your personal information to provide our customers with requested
Services and Materials and to manage our relationship with you and our customers. Where end
users’ personal information is provided to us for the purpose of us providing our Services
(e.g. when our customer issues a credential using our Services), we use and disclose that end
user personal information for service delivery and the other limited purposes set out in this
Privacy Policy. We do not use it for purposes like marketing services from us or others to
the end user. We may also use your personal information to:
1. **Communicate with you**. This may include:
1. providing you with information you’ve requested from us (like training or education
materials) or information we are required to send to you;
2. operational communications, like changes to our Services, security updates, or
assistance with using our Services;
3. marketing communications (about us or another product or Service we think you might
be interested in) if you have opted in with your marketing preferences; or
4. asking you for feedback or to take part in any research we are conducting (which we
may engage a third party to assist with).
2. **Support you**: This may include assisting with the resolution of technical support
issues or other issues relating our Services, whether by email, in-app support or
otherwise.
3. **Enhance our Services and develop new ones**: Such as providing new or improved tools
and optimising user experiences.
4. **Protect our Services and Materials**: So that we can detect and prevent any fraudulent
or malicious activity, and make sure that everyone is using our Services fairly and in
accordance with any applicable terms and conditions.
5. **Comply with legal requirements**: To comply with applicable laws, regulations or legal
processes, or to exercise, establish or defend our legal rights.
6. #### How we minimise the sharing of your personal information [#how-we-minimise-the-sharing-of-your-personal-information]
1. Where we collect personal information, we’ll only disclose it as reasonably required:
1. to provide our customers with our Services and Materials, including to other MATTR group
companies and trusted partners and service providers who are involved in the provision of
the Services or Materials or are otherwise providing goods or services to MATTR and have
agreed to protect your personal information in a manner consistent with this Privacy
Policy
2. to the extent that such disclosure is necessary for us to use the personal information
for the purpose it was collected
3. in accordance with this Privacy Policy, the Privacy Act and any other applicable data
protection and privacy laws (including as set out in the MATTR Customer Agreement and any
applicable Service Terms and Service Level Agreement)
4. to regulators, law enforcement bodies, government agencies, courts or other third parties
if required to comply with applicable laws, regulations or legal processes, or to
exercise, establish or defend our legal rights (but we’ll try to notify you about these
kinds of disclosures if possible)
5. to prevent, detect, or investigate security concerns, including fraud
6. where you are a customer representative, to an actual or potential buyer (and its agents
and advisors) in connection with an actual or proposed purchase, merger or acquisition of
any part of our business, or
7. with your consent.
7. #### International Transfers of your personal information [#international-transfers-of-your-personal-information]
1. When we do share your personal information, it may be transferred to, and processed in, a
country different to where you a located. These countries may have laws that are different to
what you are accustomed to. Where this is the case, we put comparable safeguards in place to
ensure your personal information remains protected.
2. For individuals in the European Economic Area (**EEA**), or in relation to any natural person
who is identified or identifiable and in the EEA, this means that your data may be
transferred outside of the EEA in accordance with the MATTR Data Processing Terms. For
further information, please contact us using the details set out in the “How to contact
us” section below.
8. #### Security [#security]
1. Security is a priority for us when it comes to your personal information. We’re committed to
protecting your personal information and have appropriate technical and organisational
measures in place to protect your personal information. For more information about the
security of your personal information, you can [contact us](mailto:privacy@mattr.global).
9. #### Retention [#retention]
1. The length of time we keep your personal information depends on the type of personal
information and whether we have an ongoing business need to retain it (for example, to
provide a requested Service to our customer or to comply with applicable legal or tax
requirements). We’ll retain personal information only for as long as is necessary.
10. #### Your rights [#your-rights]
1. It’s your personal information and you have rights, including to:
1. know what personal information we hold about you;
2. access the personal information we hold about you; and
3. request a correction of the personal information.
2. You can exercise these rights, and any other rights you may have under applicable data
protection and privacy laws, at any time by making a request to us. If you’re not happy with
how we are collecting, using or disclosing your personal information, please let us know by
[contacting us](mailto:privacy@mattr.global). Your requests may be subject to certain
conditions or grounds for refusal, as set out in the Privacy Act or under applicable data
protection and privacy laws. We will review and investigate your complaint and try to get
back to you within a reasonable timeframe. You can also request investigation by the New
Zealand Privacy Commissioner at any time during or after raising a complaint with us.
11. #### Changes [#changes]
1. We may need to update this Privacy Policy from time to time. We will publish the updated
version on our website. Where a change is significant, we’ll also endeavour to let our
customers know by email. Any such changes will come into effect 30 days after the updated
version is published.
12. #### How to contact us [#how-to-contact-us]
1. We’re always keen to hear from you. If you’re curious about what personal information we hold
about you or have a question or feedback for us on this Privacy Policy, please contact our
Privacy and Data Protection Officer at [privacy@mattr.global](mailto:privacy@mattr.global).
2. As a technology company, we prefer to communicate with you by email – this ensures that
you’re put in contact with the right person, in the right location, and in accordance with
any regulatory timeframes.
# MATTR Privacy Policy (archived)
URL: /docs/resources/terms/privacy-policy/5-11-24
This document is provided for archival purposes only.
Last Updated: 5 November 2024
[See what's changed](/docs/resources/terms/privacy-policy/recent-changes) |
[Previous versions](#previous-versions)
1. #### Your Privacy Matters [#your-privacy-matters]
1. MATTR is built and operated as a privacy-first company. Enhancement of privacy and trust in
digital transactions are fundamental to the software we develop. We aim to support entities
participating in the growing eco-system of privacy-preserving verifiable data transactions.
2. This Privacy Policy sets out how we collect, use, disclose and protect personal information
which we process on our own behalf (that is, as a data controller). It does not cover
situations where we process personal information on behalf of another party, or as their
agent or data processor (such as where a customer uses our Services or Materials to issue,
verify or otherwise manage credentials). In these circumstances the other entity (e.g. the
customer issuing, verifying or managing the credentials using our Services or Materials) has
its own privacy policy which will apply instead of this Privacy Policy. In the first
instance, we recommend that you contact that entity for any questions you have about your
personal information (including where you want to access, correct, amend, or request the
deletion of, your personal information).
2. #### Who we are [#who-we-are]
1. When we say “our”, “we”, or “us”, we mean MATTR Limited a New Zealand company, NZBN
9429039661098 (MATTR). Our corporate headquarters are in New Zealand, but we operate globally
and have group companies and personnel in a number of other locations. We provide easy-to use
Services and Materials to improve trust and privacy in digital interactions.
2. We carefully comply with all privacy and data protection laws applicable to our business. The
specific privacy laws that apply to our processing of your information will depend on where
you are located.
3. For clarity, when we refer to "you" or "your" we mean an individual whose personal
information is processed by us.
3. #### Our principles of data protection [#our-principles-of-data-protection]
1. **General Supply commitments:** Our approach to privacy and data protection is built around
four key principles. They’re at the heart of everything we do relating to personal
information.
1. Transparency: We take a human approach to how we process personal information by being
open, honest and transparent.
2. Security: We champion industry leading approaches to securing the personal information
entrusted to us.
3. Stewardship: We accept the responsibility that comes with processing personal
information.
4. Data minimisation: We are continuously working to minimise the personal information that
we collect and develop more privacy preserving features.
4. #### Types of personal data we collect [#types-of-personal-data-we-collect]
1. When we say “personal information” we mean identifiable information about you. If you can’t
be identified (for example, when personal information has been aggregated and anonymised)
then this Privacy Policy doesn’t apply.
2. We collect personal information when you provide it to us, when you interact with our
products and services, websites and electronic systems, when you attend events and visit our
offices, and when other sources provide it to us, as further described below:
1. **Directly provided information:** We collect personal information that you provide to us
directly. For example, if you fill out a form or sign up to our Services on our website,
we will collect any information you provide to us in that context and as you use the
Services you may input personal information in order to manage and configure your
account. You may also provide us personal information if you contact us for support,
participate in community forums, join us on social media, take part in training and
events or visit our offices. If you don’t want to provide us with such personal
information, it may mean that we cannot provide you with certain Services or Materials.
2. **Automatically collected information when using our website:** When using our website,
we collect analytics data about how you use and interact with our site, which may include
your online device information, such as the IP address and device ID of the device you
are using, the domain name from which you are accessing the internet, the operating
system and the browser your device uses, any search engine you are using, your browsing
behaviour and click activities, date and time you are visiting, location data based on
where your browsing session originates, urls of pages you visit, the domain name of pages
you visit directly before or after coming to our site, login history, performance results
for the website, details of any errors or system crashes/failures (“**Usage Data**”). We
have set out specific information about the cookies we use on our websites below at
section 5 (Our website, cookies and similar technologies).
3. **Automatically collected information when using Services:** We collect information about
the way you use or interact with our Services and Materials and actions taken via your
account, including Usage Data associated with that use. We combine that with other
personal information we hold about you (for example, your user and customer accounts with
which interactions are linked). In addition, we may collect data about any problems you
experience with our websites, applications, Services or Materials, including bug, error,
and crash reports, which can include Usage Data such as device information, location, and
user data at the time of the bug, error, and/or crash.
4. **Information collected from third parties:** We sometimes receive personal information
about you from third parties. We have summarised some of the ways we might do that below:
1. We may receive business contact information (such as name, job title, business email,
phone number, and address), social profile (such as LinkedIn) including other details
about your organization for sales and marketing purposes, to better inform you about
MATTR products and services. Typically, and subject to applicable laws, we might
receive this information from: (A) third-party marketing initiatives, such as events
where we are a sponsor, or website forms hosted by third parties that may provide
content about us; (B) where you consent to having your attendee badge scanned at an
event hosted by us or another entity; (C) companies, such as information aggregators
and similar entities, from whom we have licensed business contact information; (D)
publicly available sources, such as your social media profile, articles or interviews
you have previously given; (E) referrals; or (F) resellers and channel partners,
including those that offer joint marketing services. In some situations, we may
combine such business contact information with other non-personal and personal
information we possess or that you have provided to us for sales and marketing
purposes.
2. We may receive information from third-party platforms for various business purposes
such as research and development, organizational credit information, program
management, or technical reasons. For example, we may receive credit information
about an organization that includes the names of individuals.
3. If you are a candidate applying for a job at MATTR, subject to laws applicable in
your jurisdiction, we may receive personal information about you from third parties
for business purposes, such as through background checks (educational, employment,
criminal, and financial information), publicly-available sources (like social media
accounts, including LinkedIn for identifying candidates), feedback about your
application and from interviews, and other third parties that may provide feedback
about your application. MATTR treats references provided as part of a job application
as confidential.
5. #### Our website, cookies and similar technologies [#our-website-cookies-and-similar-technologies]
1. A cookie is a small text file that websites can ask your browser to store on your device to
enhance your experience and collect and understand Usage Data. You can disable cookies by
changing the settings on your browser, by not providing consent for us to use cookies, or by
changing your cookie preferences, but this may impact your experience of our website. We
summarise some of the cookies we use on our website below.
***Pardot***
2. We use Pardot as a marketing automation tool, which uses first-party cookies for tracking
purposes and third-party cookies for redundancy (e.g., because of differences between how web
browsers use cookies). The following cookies are saved by Pardot/Salesforce for 3650 days:
1. visitor\_id `` : **Visitor Cookie**: The visitor cookie includes the name
“visitor\_id” plus the unique identifier for MATTR, which is derived from the tracking
code on our website. The value stored is the unique ID for the visitor.
2. pi\_opt\_in `` : **Opt-in Cookie**: The persistent cookie named “pi\_opt\_in” is
used to track a visitor. The value stored is “true” or “false”. If you do not opt-in to
allow cookies to track you, or if you ignore the opt-in banner, the Visitor Cookie is
disabled and you will not be tracked when you view our pages, forms, or landing pages, or
download files or click on a custom redirect link. In some circumstances, we may track
you even if you do not accept the Opt-in Cookie. For example, we may track you through
form and landing page submissions and when you send us an email, or open or click on our
emails.
3. Visitor\_id ``-hash : This cookie is a security measure to make sure that a
malicious user can’t fake a being another visitor and access corresponding information.
4. lpv `` : This LPV cookie is set to keep Pardot from tracking multiple page
views on a single asset over a 30-minute session. For example, if you reload a landing
page several times over a 30-minute period, this cookie keeps each reload from being
tracked as a page view.
5. pardot: A session cookie named ’pardot’ is set in your browser while you’re logged in as
a user or when you access a form, landing page, or page with Pardot tracking code. The
cookie indicates an active session and isn’t used for tracking.
***6Sense***
3. We also use 6Sense to collect Usage Data in connection with business development, sales and
marketing. 6Sense may combine this Usage Data with other data it holds in order to: (i)
identify the organisation you work for; (ii) provide indications of how likely your
organization is to purchase our products or services; and (iii) suggest contact points within
your organization and provide us with business contact data in respect of these. Usage Data
that has been aggregated or de-identified may be used by 6Sense to maintain, improve or
enhance its products and services.
4. The following cookies are used in connection with 6Sense:
1. **sixSenseUUID**. Cookie name: “6suuid”. A third party cookie that contains a unique
identifier for the visitor that is managed by 6Sense. It is saved for a period of 2 years
and 12 hours.
2. **visitorUUID**. Cookie name: “\_gd\_svisitor”. A first party cookie containing a unique
identifier for the visitor that is specific to the website and will remain unique for the
duration of the cookie. It is saved for a period of 2 years and 12 hours.
3. **sessionUUID**. Cookie name: “\_gd\_session”. A first party cookie containing a unique
identifier for the visitor’s session on the website. Used to correlate page views and
activities to a single browsing session on the website. It will remain unique for the
duration of the cookie. It is saved for 4 hours.
4. **svisitorUUID**. Cookie name: “\_gd\_svisitor”. A first party cookie which is a long
lived unique identifier of this visitor on the website. It is either correlated to the
6uuid or a unique value, and can be changed during the lifetime of this cookie. It is
saved for a period of 2 years and 12 hours.
5. **retargetingUID**. Cookie name: “\_an\_uid”. A first party cookie which is a unique
identifier for the purpose of running 6sense Creative Ads. It is saved for a period of 1
week.
6. **6Sense Campaign ID**. Cookie name: “\_6si\_cid”. A first party cookie which is a unique
identifier for the vistior’s session on the website. It is used to track the account’s
activity on the website when the visitor has come to the website through a 6sense ads
campaign. It will remain unique for the duration of the cookie.
5. In addition, the following information may be stored in local browser storage in connection
with 6Sense:
1. **6Sense Company Identification**. Name: “\_6senseCompanyIdentification” which contains
metadata associated to web end-user’s IP/\_gd\_svisitor cookie;
2. **6Sense Time-to-live**. Name: “\_6SenseTTL”. A value for the period of time that
metadata provided by 6Sense (i.e. \_6SenseCompanyDetails) should exist on a computer or
network before being discarded.
6. You can find out more detailed information on how 6Sense processes personal information and
contact it to delete your information or opt out of certain processing
[here](https://6sense.com/privacy-center/).
***Google Ads***
7. We use the Google Ads platform to advertise and to understand the performance of our
advertising for marketing and sales purposes (ad conversion data). To enable this, if you
provide consent, we use cookies which may store information including:
1. a unique identifier for you (as a visitor of our website); and
2. a particular advertisement click that brought you to our website
3. whether you take particular actions after clicking through from an advertisement, such as
submitting a sales inquiry.
8. This information is shared with Google for them to provide us with ad conversion data. If you
are otherwise identifiable by Google, for example because you are signed into a Google
application or service, you may be identifiable to them and us in connection with ad
conversion data related to you. You can read more about how Google uses information from
websites like us that use Google’s services
[here](https://policies.google.com/technologies/partner-sites).
9. You can opt-out by visiting the Google Ad Setting website
[here](https://adssettings.google.com/authenticated). To learn more about Google’s privacy
practices you can read Google’s Privacy Policy and Terms website
[here](https://policies.google.com/privacy).
***Preventing bots and spam***
10. Our website is protected by the hCaptcha anti-bot service, which checks whether the data
entered on our website has been entered by a human or by an automated program. To learn
more, you can read hCaptcha’s [privacy policy](https://www.hcaptcha.com/privacy) and
[terms of service](https://www.hcaptcha.com/terms).
***Preventing bots and spam***
11. In addition to Pardot and Google Ads, which is described in the Cookies section above, we’ve
made the conscious choice to pay for a privacy-first analytics tool called Matomo. To learn
more, you can read about [privacy-focused analytics](https://matomo.org/privacy/) and
Matomo’s [privacy policy](https://matomo.org/matomo-cloud-privacy-policy/).
6. #### How we use your personal information [#how-we-use-your-personal-information]
1. We have summarised below the ways MATTR uses personal information:
1. **Service delivery:** First and foremost, we use your personal information to provide our
customer and/or you with Services and Materials and to manage our relationship with you
and our customers.
2. **Communicate with you:** We may communicate with you in respect of the following (if you
wish to cease being the point of contact for one of our customers please let us know):
1. providing you with information you’ve requested from us (like training or education
materials) or other information we need to send to you;
2. respond to you if you make an enquiry or apply for a job with us;
3. sending you operational or service communications, like changes to our Services,
release notifications, incident or security updates, assistance with using our
Services or tailored communications based on your activity and interactions with us;
4. asking you for feedback or to take part in any research we are conducting (which we
may engage a third party to assist with).
3. **Support:** We may provide technical support to customer representatives or individual
users of our customers. This may include assisting with the resolution of technical
support issues or other issues relating our Services, whether by email, in-app support or
otherwise.
4. **Enhance and improve:** We analyse and predict how our existing website, marketing
campaigns, Services and Materials are used, develop and test new or improved features,
tools, Services or Materials, marketing campaigns or promotions, optimise user
experiences and make them more personalised to you or groups of users like you.
5. **Fraud prevention and system protection:** We monitor use of our website, Services and
Materials for the purpose of detecting and preventing fraudulent or malicious activity,
and to make sure that everyone is using our Services fairly and in accordance with any
applicable terms and conditions.
6. **Business development:** We use personal information (including business contact data
and Usage Data in respect of our websites) for business development purposes. This may
include identifying the organisation you work for when browsing our website, assessing
the likelihood that your organisation may purchase from us, identifying business
opportunities, building a profile of categories of individuals or organizations likely to
be interested in our products, and introducing members of our sales team to you, sending
you tailored content, and recording our business development interactions or
communications with you and other details we have learned about you in the course of
those interactions.
7. **Marketing:** Where we have your consent and/or otherwise in accordance with applicable
law, we may use personal information (including contact data, Usage Data in respect of
(i) our website and/or (ii) our Services and Materials) to send promotional
communications that may be of interest to you and your organization, including by email
and by displaying MATTR marketing communications on other organizations’ websites and
applications, as well as on third-party platforms like Facebook, X, and Google subject to
laws applicable in your jurisdiction. These communications are aimed at encouraging
engagement and maximizing the benefits that you and your organization can gain from
MATTR’s products and services, including information about new products and features,
survey requests, newsletters, and events that we think may be of interest to you and your
organization. We may also use personal information to analyse these communication
efforts, build target audiences to ensure these communications are more effective and
relevant to each recipient. You can opt out of marketing emails at any time, by clicking
on the unsubscribe link and/or contacting us at [privacy@mattr.global](mailto:privacy@mattr.global).
8. **Recruiting and hiring:** We process personal information, such as contact details, job
applicant, and biographical data, to assess job applications and to evaluate and improve
our recruitment system, our application tracking and recruitment activities. We may also:
(i) communicate with you regarding your application or opportunities at MATTR that appear
over time that we believe may be of interest to you; and/or (ii) send you new hire and
employee experience information. We may verify your information, including through
confidential reference checks and, to the extent permitted in accordance with applicable
law, carry out background checks.
9. **Comply with legal requirements:** We process personal information to comply with
applicable laws, regulations or legal processes, demonstrate such compliance, or to
exercise, establish or defend our legal rights, respond to legal claims and undertake
compliance programmes.
10. **Other purposes in our legitimate interests:** We process personal information for
other purposes not expressly set out above where it is within the legitimate interests
of our business operations and management, including but not limited to, for operational
purposes and workflow automation, business intelligence, regulatory, and audit
functions, protecting personal property or safety.
7. #### Disclosure of your personal information [#disclosure-of-your-personal-information]
1. We may disclose personal information in the circumstances set out below.
1. **Intended purpose.** We may disclose personal information to third parties where
required/requested by you as part of providing the Services and Materials or is otherwise
necessary for us to handle the personal information for the purpose it was collected.
2. **Service providers.** We may disclose all categories of personal information to our
suppliers and service providers for various business purposes, including, but not limited
to, auditing interactions with users, debugging our websites, products and services,
security purposes, internal research and gleaning insights through machine learning,
short-term uses such as credit verification, payment processing, IT services, quality
control and safety, in-person and virtual event management, as well as to perform other
services on our behalf. We also use a range of service providers and suppliers who
process personal information on our behalf as our agent or as a data processor, including
the following: [https://learn.mattr.global/docs/resources/terms/data-sub-processors](https://learn.mattr.global/docs/resources/terms/data-sub-processors).
3. **Affiliates and professional advisers.** We disclose personal information to our
affiliates and subsidiaries for business purposes. For example to our group companies as
part of providing the Services or Materials or supporting our back office functions. We
will also disclose your personal information to our professional service providers (for
example, our auditors, insurance providers, financial service providers, and legal
advisors) as needed for us to run our business.
4. **Advertising and marketing.** In some circumstances we share personal information, such
as Usage Data and/or contact data, with third-party advertising and marketing providers,
to allow us to better reach our customers and prospective customers, and to sell our
products and services, to the extent permitted by laws applicable in your jurisdiction.
In some circumstances, we may ask you to consent to directly disclosing your personal
information with these third parties prior to sharing your personal information, such as
via a consent banner on our website.
5. **Job applications.** When you apply for a job at MATTR, we disclose your personal
information, including applicant data, biographical information, and other personal
information we possess to our affiliate companies for business reasons, such as human
resource management and internal reporting; our service providers for business reasons,
such as the recruitment platform, to verify references and to manage background checks;
and law enforcement or government authorities, or as otherwise necessary to comply with
law or as needed for the recruitment and human resources process.
6. **Law, legal process and fraud prevention.** We may disclose personal information:
1. as otherwise set out in this Privacy Policy or any other agreements or documentation
that apply to our processing of your personal information (including the MATTR
Customer Agreement, any applicable Service Terms and the Service Level Agreement);
2. to regulators, law enforcement bodies, government agencies, courts or other third
parties if required to comply with applicable laws, regulations or legal processes,
demonstrate such compliance, or to exercise, establish or defend our legal rights
(but we’ll try to notify you about these kinds of disclosures if possible);
3. to prevent, detect, investigate or mitigate security concerns, including fraud;
4. as required for us to carry out a corporate transaction, such as a merger or sale of
assets of all or part of our company (this may include, where you are a customer
representative, disclosing your personal information to an actual or potential buyer
and its agents and advisors); or
5. otherwise with your consent.
8. #### International Transfers of your personal information [#international-transfers-of-your-personal-information]
1. When we do disclose your personal information as set out above, it may be transferred to, and
processed in, a country different to where you are located. These countries may have laws
that are different to what you are accustomed to. Where this is the case, we put in place
appropriate safeguards as required by applicable law to ensure your personal information
remains protected.
2. We may also transfer your personal information to our group companies in other parts of the
world in the context of using it in accordance with this policy (in particular to our
corporate headquarters in New Zealand, where many of our personnel and back office functions
are located).
3. For individuals in the European Economic Area (EEA) or Switzerland this means that your data
may be transferred outside of the EEA or Switzerland. For further information, please contact
us using the details set out in the “How to contact us” section below.
9. #### Security [#security]
1. Security is a priority for us when it comes to your personal information. We maintain an
information security programme (including the adoption and enforcement of internal policies
and procedures) designed to protect personal information against misuse, identify reasonably
foreseeable and internal security risks and minimise security risks. For more information
about the security of your personal information, you can contact us.
10. #### Retention [#retention]
1. The length of time we keep your personal information depends on the type of personal
information and whether we have an ongoing business need to retain it (for example, to
provide a requested Service to our customer or to comply with applicable legal or tax
requirements). We’ll retain personal information only for as long as is necessary.
11. #### Your rights [#your-rights]
1. Depending on where you are located and what privacy laws apply, you have a number of rights
with respect to our use of your personal information.
2. These may include a right to access or correct your personal information, opt out of
marketing emails or other services or object to any processing of your personal information.
If you are in the European Union, you have additional rights, which we will ensure we comply
with.
3. You can exercise these rights, and any other rights you may have under applicable data
protection and privacy laws, at any time by making a request to us via email to
[privacy@mattr.global](mailto:privacy@mattr.global).
4. If you’re not happy with how we are collecting, using or disclosing your personal
information, please let us know by contacting us. Your requests may be subject to certain
conditions or grounds for refusal, as set out under applicable data protection and privacy
laws. We will review and investigate your complaint and try to get back to you within a
reasonable timeframe.
12. #### Changes [#changes]
1. We will update this Privacy Policy from time to time. When we do so we will publish the
updated version on our website.
13. #### How to contact us [#how-to-contact-us]
1. We’re always keen to hear from you. If you’re curious about what personal information we hold
about you or have a question or feedback for us on this Privacy Policy, please contact our
Privacy and Data Protection Officer at [privacy@mattr.global](mailto:privacy@mattr.global).
## Previous versions [#previous-versions]
### Privacy policy [#privacy-policy]
* [MATTR Privacy Policy - 10 November 2022 (archived)](/docs/resources/terms/privacy-policy/10-11-22)
* [MATTR Privacy Policy - 9 December 2021 (archived)](/docs/resources/terms/privacy-policy/9-12-21)
* [MATTR Privacy Policy - 25 March 2021 (archived)](/docs/resources/terms/privacy-policy/25-3-21)
## Website privacy policy [#website-privacy-policy]
1. [MATTR Website Privacy Policy - 9 December 2021 (archived)](/docs/resources/terms/website-privacy-policy/9-12-21)
2. [MATTR Website Privacy Policy - 20 July 2021 (archived)](/docs/resources/terms/website-privacy-policy/20-7-21)
3. [MATTR Website Privacy Policy - 25 March 2021 (archived)](/docs/resources/terms/website-privacy-policy/25-3-21)
# MATTR Privacy Policy (archived)
URL: /docs/resources/terms/privacy-policy/9-12-21
This document is provided for archival purposes only.
Last Updated: 9 December 2021
## Your Privacy Matters [#your-privacy-matters]
MATTR is built and operated as a privacy-first company. Enhancement of privacy and trust in digital
transactions are fundamental to the software we develop. We aim to support entities participating in
the growing eco-system of privacy-preserving verifiable data transactions. We do not sell, rent or
otherwise monetise personal information that we process on our customers’ behalf.
This privacy policy (**Privacy Policy**) sets out how we collect, use, disclose and protect personal
information when:
* we provide Services to our customers through our software-as-a-service platform (e.g., when we
issue or verify credentials)
* we provide other Services to our customers (such as training or seminars), or
* we provide access to or use of our Materials, such as apps, SDKs, and the content, materials,
software, data, documents (etc.) we make available to allow use of our Services.
(For a full definition of all of our Services and Materials, see
our [Customer Agreement](https://learn.mattr.global/docs/resources/terms/customer-agreement), which may be
updated from time to time).
First and foremost, we use your personal information to provide our customers with requested
Services and Materials and to manage our relationship with you and our customers. Where end users’
personal information is provided to us for the purpose of us providing our Services (e.g. when our
customer issues a credential using our Services), we use and disclose that end user personal
information for service delivery and the other limited purposes set out in this Privacy Policy. We
do not use it for purposes like marketing services from us or others to the end user.
If you access or use our website, sign-up to receive MATTR communications or content, or otherwise
use Materials without representing or becoming our customer,
the [MATTR Website Privacy Policy](https://learn.mattr.global/docs/resources/terms/website-privacy-policy/9-12-21)
applies.
If you don’t have a relationship with us, but believe your personal information is used by an entity
that accesses or uses our Materials or Services, that entity’s privacy policy applies to their
collection, use and disclosure of your personal information. In the first instance, we recommend
that you contact that entity for any questions you have about your personal information (including
where you want to access, correct, amend, or request the deletion of, your personal information).
1. #### Who we are [#who-we-are]
1. When we say “**our**”, “**we**”, or “**us**”, we mean MATTR Limited (**MATTR**). Our offices
are in New Zealand, but we operate globally. We provide easy-to use Services and Materials
to improve trust and privacy in digital interactions.
2. We collect, use and share personal information in accordance with the Privacy Act 2020 (NZ)
(**Privacy Act**).
3. If a customer accesses or uses our Services or Materials in the European Economic Area or
the United Kingdom (**EEA**), or in relation to any natural person who is identified or
identifiable and in the EEA,
the [MATTR Data Processing Terms](https://learn.mattr.global/docs/resources/terms/data-processing-terms)
apply to how we process that personal data.
4. For clarity, when we refer to "**you**" or "**your**" we mean an individual whose personal
information is processed using our Services or who accesses the Materials on behalf of a
customer.
2. #### Our principles of data protection [#our-principles-of-data-protection]
1. Our approach to privacy and data protection is built around four key principles. They’re at
the heart of everything we do relating to personal information.
2. **Transparency**: We take a human approach to how we process personal information by being
open, honest and transparent.
3. **Security**: We champion industry leading approaches to securing the personal information
entrusted to us.
4. **Stewardship**: We accept the responsibility that comes with processing personal
information.
5. **Data minimisation**: We are continuously working to minimise the personal information that
we collect and develop more privacy preserving features.
3. #### Types of personal data we collect [#types-of-personal-data-we-collect]
1. When we say “**personal information**” we mean identifiable information about you that we
collect when our customers access or use our Services or Materials. Examples of personal
information include name, email address, phone number, bank account details, identifiable
support queries and community comments, and so on.
2. You or our customers may disclose some of this personal information to us optionally, or we
may need it to provide the Services (for example, payment information).
3. If you can’t be identified (for example, when personal information has been aggregated and
anonymised) then this Privacy Policy doesn’t apply.
4. #### How we collect personal information [#how-we-collect-personal-information]
1. We collect personal information that we need to provide our customers with our Services or
Materials, and any information you provide to us optionally. The way we collect this personal
information can be broadly categorised into the following:
2. **Information you provide to us directly**: For example, if you use the Sign-Up feature on
our website to get access to our Services for an organisation you represent (including during
a trial period or on a preview basis), we may ask you for your name, organisation, payment
information, and email so that we can correctly assess your application and discuss your
requirements (if applicable). You may also provide us with similar contact information if you
contact us for support, participate in community forums, join us on social media, or take
part in training and events. If you don’t want to provide us with such personal information,
it may mean that we cannot provide you with certain Services or Materials.
3. **Information we collect automatically**: We may collect some personal information
automatically when our customer accesses or uses our Services and Materials, including
through the use of cookies. This can include IP addresses, interactions with our Services,
and the user and customer accounts with which interactions are linked. This personal
information helps us to operate and provide our Services and Materials, get a better
understanding of how our Services and Materials are accessed and used and may be used to
improve our Services and Materials. Note that if you’re just using or accessing our website,
the [Website Privacy Policy](https://learn.mattr.global/docs/resources/terms/website-privacy-policy/9-12-21)
4. **Information we collect from third parties**: We usually collect personal information
directly from you or from our customer. Our customer may be an agency that you have a direct
relationship with (like an educational institution or government department) or its service
provider. Sometimes we collect personal information about you from other sources, such as
publicly available materials or trusted third parties (such as research partners or the
issuer of a credential that you hold and that our Service is being used to verify). We may
also collect and use this personal information to better inform, personalise and improve our
Services.
5. #### How we use your personal information [#how-we-use-your-personal-information]
1. First and foremost, we use your personal information to provide our customers with requested
Services and Materials and to manage our relationship with you and our customers. Where end
users’ personal information is provided to us for the purpose of us providing our Services
(e.g. when our customer issues a credential using our Services), we use and disclose that end
user personal information for service delivery and the other limited purposes set out in this
Privacy Policy. We do not use it for purposes like marketing services from us or others to
the end user. We may also use your personal information to:
2. **Communicate with you**. This may include:
1. providing you with information you’ve requested from us (like training or education
materials) or information we are required to send to you;
2. operational communications, like changes to our Services, security updates, or assistance
with using our Services;
3. marketing communications (about us or another product or Service we think you might be
interested in) if you have opted in with your marketing preferences (including by
tracking your use of, and interaction with, our website and marketing emails); or
4. asking you for feedback or to take part in any research we are conducting (which we may
engage a third party to assist with).
3. **Support you**: This may include assisting with the resolution of technical support issues
or other issues relating our Services, whether by email, in-app support or otherwise.
4. **Enhance our Services and develop new ones**: Such as providing new or improved tools and
optimising user experiences.
5. **Protect our Services and Materials**: So that we can detect and prevent any fraudulent or
malicious activity, and make sure that everyone is using our Services fairly and in
accordance with any applicable terms and conditions.
6. **Comply with legal requirements**: To comply with applicable laws, regulations or legal
processes, demonstrate such compliance, or to exercise, establish or defend our legal rights.
6. #### How we minimise the sharing of your personal information [#how-we-minimise-the-sharing-of-your-personal-information]
1. Where we collect personal information, we’ll only disclose it as reasonably required:
2. to provide our customers with our Services and Materials, including to other MATTR group
companies and trusted partners and service providers who are involved in the provision of the
Services or Materials or are otherwise providing goods or services to MATTR and have agreed
to protect your personal information in a manner consistent with this Privacy Policy
3. to the extent that such disclosure is necessary for us to use the personal information for
the purpose it was collected
4. in accordance with this Privacy Policy, the Privacy Act and any other applicable data
protection and privacy laws (including as set out in the MATTR Customer Agreement and any
applicable Service Terms and Service Level Agreement)
5. to regulators, law enforcement bodies, government agencies, courts or other third parties if
required to comply with applicable laws, regulations or legal processes, demonstrate such
compliance, or to exercise, establish or defend our legal rights (but we’ll try to notify you
about these kinds of disclosures if possible)
6. to prevent, detect, or investigate security concerns, including fraud
7. where you are a customer representative, to an actual or potential buyer (and its agents and
advisors) in connection with an actual or proposed purchase, merger or acquisition of any
part of our business, or
8. with your consent.
7. #### International Transfers of your personal information [#international-transfers-of-your-personal-information]
1. When we do share your personal information, it may be transferred to, and processed in, a
country different to where you a located. These countries may have laws that are different to
what you are accustomed to. Where this is the case, we put comparable safeguards in place to
ensure your personal information remains protected.
2. For individuals in the European Economic Area (**EEA**), or in relation to any natural person
who is identified or identifiable and in the EEA, this means that your data may be
transferred outside of the EEA in accordance with the MATTR Data Processing Terms. For
further information, please contact us using the details set out in the “How to contact
us” section below.
8. #### Security [#security]
1. Security is a priority for us when it comes to your personal information. We’re committed to
protecting your personal information and have appropriate technical and organisational
measures in place to protect your personal information. For more information about the
security of your personal information, you can [contact us](mailto:privacy@mattr.global).
9. #### Retention [#retention]
1. The length of time we keep your personal information depends on the type of personal
information and whether we have an ongoing business need to retain it (for example, to
provide a requested Service to our customer or to comply with applicable legal or tax
requirements). We’ll retain personal information only for as long as is necessary.
10. #### Your rights [#your-rights]
1. It’s your personal information and you have rights, including to:
1. **know** what personal information we hold about you;
2. **access** the personal information we hold about you; and
3. **request** a correction of the personal information.
2. You can exercise these rights, and any other rights you may have under applicable data
protection and privacy laws, at any time by making a request to us. If you’re not happy with
how we are collecting, using or disclosing your personal information, please let us know by
[contacting us](mailto:privacy@mattr.global). Your requests may be subject to certain
conditions or grounds for refusal, as set out in the Privacy Act or under applicable data
protection and privacy laws. We will review and investigate your complaint and try to get
back to you within a reasonable timeframe. You can also request investigation by the New
Zealand Privacy Commissioner at any time during or after raising a complaint with us.
11. #### Changes [#changes]
1. We may need to update this Privacy Policy from time to time. We will publish the updated
version on our website. Where a change is significant, we’ll also endeavour to let our
customers know by email. Any such changes will come into effect 30 days after the updated
version is published.
12. #### How to contact us [#how-to-contact-us]
1. We’re always keen to hear from you. If you’re curious about what personal information we hold
about you or have a question or feedback for us on this Privacy Policy, please contact our
Privacy and Data Protection Officer at [privacy@mattr.global](mailto:privacy@mattr.global).
2. As a technology company, we prefer to communicate with you by email – this ensures that
you’re put in contact with the right person, in the right location, and in accordance with
any regulatory timeframes.
# MATTR Privacy Policy
URL: /docs/resources/terms/privacy-policy
Last Updated: 16 April 2026
[See what's changed](/docs/resources/terms/privacy-policy/recent-changes) |
[Previous versions](#previous-versions)
1. #### Your Privacy Matters [#your-privacy-matters]
1. MATTR is built and operated as a privacy-first company. Enhancement of privacy and trust in
digital transactions are fundamental to the software we develop. We aim to support entities
participating in the growing eco-system of privacy-preserving verifiable data transactions.
2. This Privacy Policy sets out how we collect, use, disclose and protect personal information
which we process on our own behalf (that is, as a data controller). It does not cover
situations where we process personal information on behalf of another party, or as their
agent or data processor (such as where a customer uses our Services or Materials to issue,
verify or otherwise manage credentials). In these circumstances the other entity (e.g. the
customer issuing, verifying or managing the credentials using our Services or Materials) has
its own privacy policy which will apply instead of this Privacy Policy. In the first
instance, we recommend that you contact that entity for any questions you have about your
personal information (including where you want to access, correct, amend, or request the
deletion of, your personal information).
2. #### Who we are [#who-we-are]
1. When we say “our”, “we”, or “us”, we mean MATTR Limited a New Zealand company, NZBN
9429047578432 (MATTR). Our corporate headquarters are in New Zealand, but we operate globally
and have group companies and personnel in a number of other locations. We provide easy-to use
Services and Materials to improve trust and privacy in digital interactions.
2. We carefully comply with all privacy and data protection laws applicable to our business. The
specific privacy laws that apply to our processing of your information will depend on where
you are located.
3. For clarity, when we refer to "you" or "your" we mean an individual whose personal
information is processed by us.
3. #### Our principles of data protection [#our-principles-of-data-protection]
1. **General Supply commitments:** Our approach to privacy and data protection is built around
four key principles. They’re at the heart of everything we do relating to personal
information.
1. Transparency: We take a human approach to how we process personal information by being
open, honest and transparent.
2. Security: We champion industry leading approaches to securing the personal information
entrusted to us.
3. Stewardship: We accept the responsibility that comes with processing personal
information.
4. Data minimisation: We are continuously working to minimise the personal information that
we collect and develop more privacy preserving features.
4. #### Types of personal data we collect [#types-of-personal-data-we-collect]
1. When we say “personal information” we mean identifiable information about you. If you can’t
be identified (for example, when personal information has been aggregated and anonymised)
then this Privacy Policy doesn’t apply.
2. We collect personal information when you provide it to us, when you interact with our
products and services, websites and electronic systems, when you attend events and visit our
offices, and when other sources provide it to us, as further described below:
1. **Directly provided information:** We collect personal information that you provide to us
directly. For example, if you fill out a form or sign up to our Services on our website,
we will collect any information you provide to us in that context and as you use the
Services you may input personal information in order to manage and configure your
account. You may also provide us personal information if you contact us for support,
participate in community forums, join us on social media, take part in training and
events or visit our offices. If you don’t want to provide us with such personal
information, it may mean that we cannot provide you with certain Services or Materials.
2. **Automatically collected information when using our website:** When using our website,
we collect analytics data about how you use and interact with our site, which may include
your online device information, such as the IP address and device ID of the device you
are using, the domain name from which you are accessing the internet, the operating
system and the browser your device uses, any search engine you are using, your browsing
behaviour and click activities, date and time you are visiting, location data based on
where your browsing session originates, urls of pages you visit, the domain name of pages
you visit directly before or after coming to our site, login history, performance results
for the website, details of any errors or system crashes/failures (“**Usage Data**”). We
have set out specific information about the cookies we use on our websites below at
section 5 (Our website, cookies and similar technologies).
3. **Automatically collected information when using Services:** We collect information about
the way you use or interact with our Services and Materials and actions taken via your
account, including Usage Data associated with that use. We combine that with other
personal information we hold about you (for example, your user and customer accounts with
which interactions are linked). In addition, we may collect data about any problems you
experience with our websites, applications, Services or Materials, including bug, error,
and crash reports, which can include Usage Data such as device information, location, and
user data at the time of the bug, error, and/or crash.
4. **Information collected from third parties:** We sometimes receive personal information
about you from third parties. We have summarised some of the ways we might do that below:
1. We may receive business contact information (such as name, job title, business email,
phone number, and address), social profile (such as LinkedIn) including other details
about your organization for sales and marketing purposes, to better inform you about
MATTR products and services. Typically, and subject to applicable laws, we might
receive this information from: (A) third-party marketing initiatives, such as events
where we are a sponsor, or website forms hosted by third parties that may provide
content about us; (B) where you consent to having your attendee badge scanned at an
event hosted by us or another entity; (C) companies, such as information aggregators
and similar entities, from whom we have licensed business contact information; (D)
publicly available sources, such as your social media profile, articles or interviews
you have previously given; (E) referrals; or (F) resellers and channel partners,
including those that offer joint marketing services. In some situations, we may
combine such business contact information with other non-personal and personal
information we possess or that you have provided to us for sales and marketing
purposes.
2. We may receive information from third-party platforms for various business purposes
such as research and development, organizational credit information, program
management, or technical reasons. For example, we may receive credit information
about an organization that includes the names of individuals.
3. If you are a candidate applying for a job at MATTR, subject to laws applicable in
your jurisdiction, we may receive personal information about you from third parties
for business purposes, such as through background checks (educational, employment,
criminal, and financial information), publicly-available sources (like social media
accounts, including LinkedIn for identifying candidates), feedback about your
application and from interviews, and other third parties that may provide feedback
about your application. MATTR treats references provided as part of a job application
as confidential.
5. #### Our website, cookies and similar technologies [#our-website-cookies-and-similar-technologies]
1. A cookie is a small text file that websites can ask your browser to store on your device to
enhance your experience and collect and understand Usage Data. You can disable cookies by
changing the settings on your browser, by not providing consent for us to use cookies, or by
changing your cookie preferences, but this may impact your experience of our website. We
summarise some of the cookies we use on our website below.
***Google Ads***
2. We use the Google Ads platform to advertise and to understand the performance of our
advertising for marketing and sales purposes (ad conversion data). To enable this, if you
provide consent, we use cookies which may store information including:
1. a unique identifier for you (as a visitor of our website); and
2. a particular advertisement click that brought you to our website
3. whether you take particular actions after clicking through from an advertisement, such as
submitting a sales inquiry.
3. This information is shared with Google for them to provide us with ad conversion data. If you
are otherwise identifiable by Google, for example because you are signed into a Google
application or service, you may be identifiable to them and us in connection with ad
conversion data related to you. You can read more about how Google uses information from
websites like us that use Google’s services
[here](https://policies.google.com/technologies/partner-sites).
4. You can opt-out by visiting the Google Ad Setting website
[here](https://adssettings.google.com/authenticated). To learn more about Google’s privacy
practices you can read Google’s Privacy Policy and Terms website
[here](https://policies.google.com/privacy).
***Preventing bots and spam***
5. We use Google reCAPTCHA on our website (integrated through our service provider HubSpot) to prevent spam and to protect against automated abuse. This service helps us distinguish between human visitors and automated bots. Your use of reCAPTCHA is subject to Google's [Cloud Terms of Service](https://cloud.google.com/terms) and [Cloud Data Processing Addendum](https://cloud.google.com/terms/data-processing-addendum).
***Privacy-focused analytics***
6. In addition to Google Ads, which is described in the Cookies section above, we’ve
made the conscious choice to pay for a privacy-first analytics tool called Matomo. To learn
more, you can read about [privacy-focused analytics](https://matomo.org/privacy/) and
Matomo’s [privacy policy](https://matomo.org/matomo-cloud-privacy-policy/).
6. #### How we use your personal information [#how-we-use-your-personal-information]
1. We have summarised below the ways MATTR uses personal information:
1. **Service delivery:** First and foremost, we use your personal information to provide our
customer and/or you with Services and Materials and to manage our relationship with you
and our customers.
2. **Communicate with you:** We may communicate with you in respect of the following (if you
wish to cease being the point of contact for one of our customers please let us know):
1. providing you with information you’ve requested from us (like training or education
materials) or other information we need to send to you;
2. respond to you if you make an enquiry or apply for a job with us;
3. sending you operational or service communications, like changes to our Services,
release notifications, incident or security updates, assistance with using our
Services or tailored communications based on your activity and interactions with us;
4. asking you for feedback or to take part in any research we are conducting (which we
may engage a third party to assist with).
3. **Support:** We may provide technical support to customer representatives or individual
users of our customers. This may include assisting with the resolution of technical
support issues or other issues relating our Services, whether by email, in-app support or
otherwise.
4. **Enhance and improve:** We analyse and predict how our existing website, marketing
campaigns, Services and Materials are used, develop and test new or improved features,
tools, Services or Materials, marketing campaigns or promotions, optimise user
experiences and make them more personalised to you or groups of users like you.
5. **Fraud prevention and system protection:** We monitor use of our website, Services and
Materials for the purpose of detecting and preventing fraudulent or malicious activity,
and to make sure that everyone is using our Services fairly and in accordance with any
applicable terms and conditions.
6. **Business development:** We use personal information (including business contact data
and Usage Data in respect of our websites) for business development purposes. This may
include identifying the organisation you work for when browsing our website, assessing
the likelihood that your organisation may purchase from us, identifying business
opportunities, building a profile of categories of individuals or organizations likely to
be interested in our products, and introducing members of our sales team to you, sending
you tailored content, and recording our business development interactions or
communications with you and other details we have learned about you in the course of
those interactions.
7. **Marketing:** Where we have your consent and/or otherwise in accordance with applicable
law, we may use personal information (including contact data, Usage Data in respect of
(i) our website and/or (ii) our Services and Materials) to send promotional
communications that may be of interest to you and your organization, including by email
and by displaying MATTR marketing communications on other organizations’ websites and
applications, as well as on third-party platforms like Facebook, X, and Google subject to
laws applicable in your jurisdiction. These communications are aimed at encouraging
engagement and maximizing the benefits that you and your organization can gain from
MATTR’s products and services, including information about new products and features,
survey requests, newsletters, and events that we think may be of interest to you and your
organization. We may also use personal information to analyse these communication
efforts, build target audiences to ensure these communications are more effective and
relevant to each recipient. You can opt out of marketing emails at any time, by clicking
on the unsubscribe link and/or contacting us at [privacy@mattr.global](mailto:privacy@mattr.global).
8. **Recruiting and hiring:** We process personal information, such as contact details, job
applicant, and biographical data, to assess job applications and to evaluate and improve
our recruitment system, our application tracking and recruitment activities. We may also:
(i) communicate with you regarding your application or opportunities at MATTR that appear
over time that we believe may be of interest to you; and/or (ii) send you new hire and
employee experience information. We may verify your information, including through
confidential reference checks and, to the extent permitted in accordance with applicable
law, carry out background checks.
9. **Comply with legal requirements:** We process personal information to comply with
applicable laws, regulations or legal processes, demonstrate such compliance, or to
exercise, establish or defend our legal rights, respond to legal claims and undertake
compliance programmes.
10. **Other purposes in our legitimate interests:** We process personal information for
other purposes not expressly set out above where it is within the legitimate interests
of our business operations and management, including but not limited to, for operational
purposes and workflow automation, business intelligence, regulatory, and audit
functions, protecting personal property or safety.
7. #### Disclosure of your personal information [#disclosure-of-your-personal-information]
1. We may disclose personal information in the circumstances set out below.
1. **Intended purpose.** We may disclose personal information to third parties where
required/requested by you as part of providing the Services and Materials or is otherwise
necessary for us to handle the personal information for the purpose it was collected.
2. **Service providers.** We may disclose all categories of personal information to our
suppliers and service providers for various business purposes, including, but not limited
to, auditing interactions with users, debugging our websites, products and services,
security purposes, internal research and gleaning insights through machine learning,
short-term uses such as credit verification, payment processing, IT services, quality
control and safety, in-person and virtual event management, as well as to perform other
services on our behalf. We also use a range of service providers and suppliers who
process personal information on our behalf as our agent or as a data processor, including
the following: [https://learn.mattr.global/docs/resources/terms/data-sub-processors](https://learn.mattr.global/docs/resources/terms/data-sub-processors).
3. **Affiliates and professional advisers.** We disclose personal information to our
affiliates and subsidiaries for business purposes. For example to our group companies as
part of providing the Services or Materials or supporting our back office functions. We
will also disclose your personal information to our professional service providers (for
example, our auditors, insurance providers, financial service providers, and legal
advisors) as needed for us to run our business.
4. **Advertising and marketing.** In some circumstances we share personal information, such
as Usage Data and/or contact data, with third-party advertising and marketing providers,
to allow us to better reach our customers and prospective customers, and to sell our
products and services, to the extent permitted by laws applicable in your jurisdiction.
In some circumstances, we may ask you to consent to directly disclosing your personal
information with these third parties prior to sharing your personal information, such as
via a consent banner on our website.
5. **Job applications.** When you apply for a job at MATTR, we disclose your personal
information, including applicant data, biographical information, and other personal
information we possess to our affiliate companies for business reasons, such as human
resource management and internal reporting; our service providers for business reasons,
such as the recruitment platform, to verify references and to manage background checks;
and law enforcement or government authorities, or as otherwise necessary to comply with
law or as needed for the recruitment and human resources process.
6. **Law, legal process and fraud prevention.** We may disclose personal information:
1. as otherwise set out in this Privacy Policy or any other agreements or documentation
that apply to our processing of your personal information (including the MATTR
Customer Agreement, any applicable Service Terms and the Service Level Agreement);
2. to regulators, law enforcement bodies, government agencies, courts or other third
parties if required to comply with applicable laws, regulations or legal processes,
demonstrate such compliance, or to exercise, establish or defend our legal rights
(but we’ll try to notify you about these kinds of disclosures if possible);
3. to prevent, detect, investigate or mitigate security concerns, including fraud;
4. as required for us to carry out a corporate transaction, such as a merger or sale of
assets of all or part of our company (this may include, where you are a customer
representative, disclosing your personal information to an actual or potential buyer
and its agents and advisors); or
5. otherwise with your consent.
8. #### International Transfers of your personal information [#international-transfers-of-your-personal-information]
1. When we do disclose your personal information as set out above, it may be transferred to, and
processed in, a country different to where you are located. These countries may have laws
that are different to what you are accustomed to. Where this is the case, we put in place
appropriate safeguards as required by applicable law to ensure your personal information
remains protected.
2. We may also transfer your personal information to our group companies in other parts of the
world in the context of using it in accordance with this policy (in particular to our
corporate headquarters in New Zealand, where many of our personnel and back office functions
are located).
3. For individuals in the European Economic Area (EEA) or Switzerland this means that your data
may be transferred outside of the EEA or Switzerland. For further information, please contact
us using the details set out in the “How to contact us” section below.
9. #### Security [#security]
1. Security is a priority for us when it comes to your personal information. We maintain an
information security programme (including the adoption and enforcement of internal policies
and procedures) designed to protect personal information against misuse, identify reasonably
foreseeable and internal security risks and minimise security risks. For more information
about the security of your personal information, you can contact us.
10. #### Retention [#retention]
1. The length of time we keep your personal information depends on the type of personal
information and whether we have an ongoing business need to retain it (for example, to
provide a requested Service to our customer or to comply with applicable legal or tax
requirements). We’ll retain personal information only for as long as is necessary.
11. #### Your rights [#your-rights]
1. Depending on where you are located and what privacy laws apply, you have a number of rights
with respect to our use of your personal information.
2. These may include a right to access or correct your personal information, opt out of
marketing emails or other services or object to any processing of your personal information.
If you are in the European Union, you have additional rights, which we will ensure we comply
with.
3. You can exercise these rights, and any other rights you may have under applicable data
protection and privacy laws, at any time by making a request to us via email to
[privacy@mattr.global](mailto:privacy@mattr.global).
4. If you’re not happy with how we are collecting, using or disclosing your personal
information, please let us know by contacting us. Your requests may be subject to certain
conditions or grounds for refusal, as set out under applicable data protection and privacy
laws. We will review and investigate your complaint and try to get back to you within a
reasonable timeframe.
12. #### Changes [#changes]
1. We will update this Privacy Policy from time to time. When we do so we will publish the
updated version on our website.
13. #### How to contact us [#how-to-contact-us]
1. We’re always keen to hear from you. If you’re curious about what personal information we hold
about you or have a question or feedback for us on this Privacy Policy, please contact our
Privacy and Data Protection Officer at [privacy@mattr.global](mailto:privacy@mattr.global).
## Previous versions [#previous-versions]
### Privacy policy [#privacy-policy]
* [MATTR Privacy Policy - 17 October 2025 (archived)](/docs/resources/terms/privacy-policy/17-10-25)
* [MATTR Privacy Policy - 5 November 2024 (archived)](/docs/resources/terms/privacy-policy/5-11-24)
* [MATTR Privacy Policy - 10 November 2022 (archived)](/docs/resources/terms/privacy-policy/10-11-22)
* [MATTR Privacy Policy - 9 December 2021 (archived)](/docs/resources/terms/privacy-policy/9-12-21)
* [MATTR Privacy Policy - 25 March 2021 (archived)](/docs/resources/terms/privacy-policy/25-3-21)
### Website privacy policy [#website-privacy-policy]
* [MATTR Website Privacy Policy - 9 December 2021 (archived)](/docs/resources/terms/website-privacy-policy/9-12-21)
* [MATTR Website Privacy Policy - 20 July 2021 (archived)](/docs/resources/terms/website-privacy-policy/20-7-21)
* [MATTR Website Privacy Policy - 25 March 2021 (archived)](/docs/resources/terms/website-privacy-policy/25-3-21)
# MATTR Privacy Policy - What's Changed
URL: /docs/resources/terms/privacy-policy/recent-changes
## Changes posted April 16, 2026 [#changes-posted-april-16-2026]
MATTR updated the [Privacy Policy](/docs/resources/terms/privacy-policy) to reflect the change from hCaptcha to reCAPTCHA as the anti-bot service used on our website.
## Changes posted October 17, 2025 [#changes-posted-october-17-2025]
MATTR updated the [Privacy Policy](/docs/resources/terms/privacy-policy) to remove references to Pardot, Salesforce and 6Sense who are no longer used as part of MATTR's technology stack.
## Changes posted November 5, 2024 [#changes-posted-november-5-2024]
MATTR updated the [Privacy Policy](/docs/resources/terms/privacy-policy) to include the (previously separate)
Website Privacy Policy and make changes to reflect a number of product and website changes as well
as MATTR's continued international expansion.
## Changes posted November 10, 2022 [#changes-posted-november-10-2022]
With the addition of customers and users in new regions, MATTR updated the
[Privacy Policy](/docs/resources/terms/privacy-policy) to reflect the application of local data protection
laws.
## Changes posted December 9, 2021 [#changes-posted-december-9-2021]
MATTR updated the [Privacy Policy](/docs/resources/terms/privacy-policy) to include reference to the Website
Privacy Policy.
## Changes posted March 25, 2021 [#changes-posted-march-25-2021]
MATTR updated the [MATTR Privacy Policy](/docs/resources/terms/privacy-policy) on March 25, 2021.
The [MATTR Privacy Policy](/docs/resources/terms/privacy-policy) was published.
Your use of our Services after the effective date of this update confirms that you have read and
understood the updated MATTR Privacy Policy.
# MATTR Pi SDK Trial Licence Agreement
URL: /docs/resources/terms/sdk-licence-agreement
Last Updated: 16 Dec 2024
**IMPORTANT**: THIS MATTR Pi SDK TRIAL LICENCE AGREEMENT APPLIES TO USE OF ALL OR ANY PART OF ANY
SOFTWARE DEVELOPMENT KITS SPECIFIED IN ANNEX A OR WHICH SPECIFY THEY ARE SUBJECT TO THIS AGREEMENT.
BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING OR ACCESSING A SOFTWARE DEVELOPMENT KIT
SUBJECT TO THIS AGREEMENT, YOU AGREE TO BE BOUND BY THE TERMS OF THIS AGREEMENT. IF YOU DO NOT
ACCEPT THIS AGREEMENT YOU MUST NOT DOWNLOAD, INSTALL, COPY OR OTHERWISE USE OR ACCESS THE SOFTWARE.
1. #### Licence [#licence]
1. This MATTR Pi SDK Trial Licence Agreement (**SDK Agreement**) applies to all or any part of a
software development kit specified in Annex A or which otherwise states it is subject to this
SDK Agreement, including any associated documentation and any updates or modifications that
we publish or make available to you (**Software**).
2. Your use of the Software is subject to:
1. the terms of the MATTR Customer Agreement available at
[https://learn.mattr.global/docs/resources/terms/customer-agreement](https://learn.mattr.global/docs/resources/terms/customer-agreement)
(**Customer Agreement**); and
2. this SDK Agreement,
together, "**the Agreement**".
3. By downloading and using any Software or by signing this SDK Agreement, you agree to the
terms of both the Customer Agreement and this SDK Agreement. The Customer Agreement will
apply between you and MATTR in respect of any other MATTR products and/or Services you
consume whether in connection with the Software or otherwise. The SDK Agreement will
constitute the “Service Terms” applicable to your use of the Software for the purposes of the
Customer Agreement. Capitalised terms used in this SDK Agreement that are not defined, have
the meaning given to them in the Customer Agreement.
4. Subject to clause 1.5, we grant you a non-exclusive non-sublicensable non-transferable
licence to use the Software solely:
1. in accordance with this Agreement; and
2. for the permitted purpose set out in clause 4.
5. You must not, without our prior written consent:
1. provide the Software or a copy of the Software to any third party for any reason;
2. distribute, sub-licence, modify, resell, rent, lease, make available to third parties, or
otherwise deal in the Software;
3. remove any copyright, patent or other proprietary notices contained in the Software; or
4. use the Software for any commercial purpose.
6. You must comply with any additional conditions or obligations we have separately specified as
applying to the Software (including in any instructions or conditions made available on our
website or at the point of download of the Software).
2. #### Intellectual Property [#intellectual-property]
1. All Intellectual Property in the Software is owned by MATTR or its licensors or assignees.
Other than as expressly provided to the contrary, nothing in this Agreement operates to
transfer or assign ownership of Intellectual Property, or confers on you any right, title or
interest in or to any of MATTR’s Intellectual Property or to any third party Intellectual
Property.
2. If you create any applications using the Software, that rely on the Software or that
incorporate any elements or components of the Software:
1. subject to clause 2.2(b), you will own the Intellectual Property in such applications;
and
2. MATTR will retain all Intellectual Property in, and this SDK Agreement (including
clause 1) will apply to the Software and any element or component of the Software
incorporated into or relied upon by your applications.
3. If you give us ideas, comments or suggestions relating to the Software (**feedback**), all
intellectual property rights that might subsist in the feedback are owned solely by us. We
may use or disclose any feedback for any purpose, without your approval, and without any
attribution or payment to you.
3. #### Term [#term]
Your right to use any Software under this SDK Agreement expires on the earlier of:
1. 180 days from the date on which you first download, install, copy or otherwise use or access
the Software; and
2. the date on which we notify you of the termination of your right to use the Software.
When your right to use any Software expires, you must immediately stop using the Software, and
must return or destroy all copies of it in your possession or control.
4. #### Permitted purpose [#permitted-purpose]
Subject to clause 5, the only permitted purpose you may use the Software for is non-commercial,
non-production, use including use in non-production proof of concepts and demonstrations. Use of
the Software is solely on a trial basis and is subject to all applicable terms in the Customer
Agreement that apply to trial usage. Without limiting any obligations under the Customer
Agreement, you agree that you will enter only dummy or test data in the Software and use it for
the sole purpose of evaluating the capabilities of the Software. You will ensure that all data
entered in the Software by you or users of the Software you create is not data that is, or is
likely to constitute, personal information or confidential information.
5. #### Fair dealing [#fair-dealing]
You may have "fair dealing" rights for the Software under the law. This SDK Agreement does not
limit them to the extent that such law provides that those rights cannot be excluded, restricted
or modified.
6. #### No other rights [#no-other-rights]
You must not copy, reverse-engineer, decompile, disassemble, attempt to derive the source code
of, or modify the Software, or any part of the Software, except to the extent that:
1. any such restriction is prohibited by applicable law; or
2. in the case of copying, such copying is for the permitted purpose as set out in clause 4.
7. #### IP challenges and patent claims [#ip-challenges-and-patent-claims]
1. You must not challenge our ownership of Intellectual Property that may subsist in the
Software or any other item or material created or developed by us or on our behalf under or
in connection with the Software.
2. If you make any written claim that the Software infringes or contributes to infringement of
any patent, the licence to the Software that has been granted to you under this SDK Agreement
ends immediately.
3. You must notify us in writing immediately after becoming aware of any circumstance which may
suggest that any person may have unauthorised knowledge, possession or use of the Software.
8. #### Open source software [#open-source-software]
1. The Software may include elements subject to one or more open source licences (**Open Source
Software**). Any such Open Source Software is licensed under its applicable licence terms.
For a full list of Open Source Software used in the Software, including third-party notices
containing credits or attributions where required for the use or redistribution of such Open
Source Software, please visit
[https://learn.mattr.global/docs/holding/go-hold/libraries](https://learn.mattr.global/docs/holding/go-hold/libraries) for
wallet or hold SDKs and/or
[https://learn.mattr.global/docs/verification/go-verify/libraries](https://learn.mattr.global/docs/verification/go-verify/libraries)
for verifier SDKs.
2. Even where Open Source Software is governed by other licence terms, the limitations of
liability set out in clause 12 and the Customer Agreement will continue to apply as between
you and MATTR, except to the extent that any such limitations are prohibited by applicable
law.
9. #### Termination for breach [#termination-for-breach]
If we notify you in writing that you have breached the Agreement, you must remedy that breach to
our reasonable satisfaction within 14 days of receiving our notice. If you don’t do so, we may
terminate either this SDK Agreement or the entire Agreement (at our option) for breach
immediately by written notice to you. This SDK Agreement will automatically terminate if the
Customer Agreement between us expires or is terminated for any reason. Except to the extent
expressly stated in a termination notice, termination or expiry of the SDK Agreement will not
affect other products or services used pursuant to the Customer Agreement and/or any other
Service Terms.
10. #### Other rights to terminate [#other-rights-to-terminate]
We can terminate this SDK Agreement at any time, for any reason or no reason at all, by giving
you written notice of termination.
11. #### Changes to these terms [#changes-to-these-terms]
We may amend this SDK Agreement at any time, with notice to you. By continuing to use the
Software after such notice, you agree to the terms as amended. If you do not agree to the terms
as amended, you must immediately cease your use of the Software.
12. #### No Liability [#no-liability]
1. We do not represent or warrant that the Software will operate without interruption or be
error-free, free from design defects or other defects, non-infringing, of any particular
quality, fit for any particular purpose or free from viruses or other malicious code.
2. To the maximum extent permitted by law, except as expressly provided in this SDK Agreement,
the Software is provided on an “as is” and “as available” basis, without express or implied
warranties, guarantees or conditions of any kind.
3. Each party’s liability to the other party in respect of the Software is governed by the
Customer Agreement.
4. Except where we have entered into a separate written agreement with you, the Software is not
provided with support services of any kind.
13. #### Other MATTR products and services [#other-mattr-products-and-services]
On your request, we may create, activate and configure accounts, tenants, and/or other MATTR
products and/or services for you to use whether in connection with the Software or otherwise. To
the extent you request this, we will carry out these activities on your behalf as your agent and
subject to clause 2.2 of the Customer Agreement.
14. #### General [#general]
1. You warrant that all information you have provided us with in connection with requesting
access to the Software is true, correct and not misleading, including: (i) the correct legal
names of each individual and/or organisation; and (ii) your intended use of the Software.
2. You agree that we may, at reasonable times on reasonable notice, audit your premises,
systems, files and applications to verify your compliance with this SDK Agreement.
3. In this SDK Agreement:
1. “we”, “us”, “our” or “MATTR” refers to the applicable MATTR contracting entity under the
Customer Agreement, which is the entity entering into this SDK Agreement with you; and
2. “you” or “your” are to the customer specified below.
I agree (on behalf of my organisation, if applicable) to the terms of this SDK Agreement and the
Customer Agreement:
**Customer Name**:
**Signature**:
**Date:**
**Name**:
**Title**:
## ANNEX A: SOFTWARE DEVELOPMENT KITS SUBJECT TO THIS AGREEMENT [#annex-a-software-development-kits-subject-to-this-agreement]
All MATTR Pi Software Development Kits and any of the following:
* All MATTR Verifier SDKs, including any version, variant, additional component, add-on and/or
library (whether beta, tech preview or general availability), for any platform, language or
operating system (e.g. web, iOS or React Native) or any credential type; and
* All MATTR Wallet or Hold SDKs, including any version, variant, additional component, add-on
and/or library (whether beta, tech preview or general availability), for any platform, language
or operating system (e.g. iOS or React Native) or any credential type,
in each case except to the extent alternative licence terms are agreed with us for that SDK.
# MATTR Service Level Agreement
URL: /docs/resources/terms/service-level-agreement
Last Updated: 2 July 2025
MATTR offers a range of support options to meet your needs. For further
information, please [contact us](https://mattr.global/contact-us).
# MATTR Service Level Agreement
URL: /docs/resources/terms/service-level-agreement/sla
*Last Updated: 25 March 2021*
This MATTR Service Level Agreement (**SLA**) governs your use of the public cloud deployment of the
Services. It forms part of the Agreement defined in the MATTR Customer Agreement document.
Capitalised terms used but not defined in this SLA have the meanings set out in the MATTR Customer
Agreement document. Other definitions are set out below.
If you require an SLA commitment or alternative deployment options, such as a dedicated instance of
the Services (private cloud, on-premises, etc.) please contact us.
1. #### Service Commitment [#service-commitment]
We pride ourselves on providing an outstanding customer experience. This starts with the way
we’ve designed our Services and extends to the way we work together with our customers during
and after deployment. We will use reasonable efforts to ensure that each Service to which this
SLA applies is Available at least 99.90% of the time during any calendar month (the **Service
Commitment**). If we do not meet the Service Commitment for a Service, you may apply for a
Service Credit (as described below). Service Credits are available for Severity Level 1
Incidents that impact Availability.
2. #### Service Credits [#service-credits]
If a Service does not meet the Service Commitment in a calendar month, the Service Credit will
be equal to a percentage, as specified in the following table, of the sum of the monthly
recurring fees and usage fees, and excluding one-time charges, for that Service in that month
(**total monthly charge**). If recurring charges and usage charges for a Service are specified
annually, the **total monthly charge** will be the sum of the annual recurring charges and usage
charges divided by 12. If a Severity Level 1 Incident spans multiple billing periods, the
Service Credit will be calculated based only on the total monthly charges for the billing period
in which that Incident commenced.
| Availability | Service Credit Percentage (of total monthly charge) |
| :------------: | :-------------------------------------------------: |
| 98.5% - 99.89% | 5% |
| 97% - 98.49% | 10% |
| \< 97% | 20% |
If you are eligible and apply for any Service Credits, we will provide them only as credits
against future Services payments otherwise due from you. Service Credits will not entitle you to
any refund or other payment from us. A Service Credit will be applicable and issued only if the
credit amount for the applicable calendar month is greater than one dollar (\$1 USD). The Service
Credit must be used within twelve months of issuance and refunds will not be provided. Service
Credits may not be transferred or applied to any other Account. For any Unavailability or other
failure to meet the Service Commitment, your sole and exclusive remedy is the receipt of a
Service Credit (if eligible) in accordance with the terms of this SLA.
3. #### Service Credit Request and Payment Procedures [#service-credit-request-and-payment-procedures]
To receive a Service Credit, you must submit a claim by sending an email to
[accounts@mattr.global](mailto:accounts@mattr.global). To be eligible, the credit request must
be received by us within 30 calendar days after which the incident occurred and must include:
1. the words “SLA Credit Request” in the subject line;
2. the dates, times, and the regions of each Incident that you are claiming caused an
Unavailability; and
3. the affected aspect of the Service in accordance with the categories outlined in the Incident
Management Table below.
If the Unavailability in your request is confirmed by us and reflects Availability that is less
than the Service Commitment, we will provide the Service Credit against the fees owed by you in
the next billing period. We do not have to provide a Service Credit if you fail to submit the
Service Credit request and other information in accordance with the terms of this SLA.
4. #### Incident Management [#incident-management]
If an Incident (as defined below) is detected by us or reported by you, we will at our
discretion confirm the Severity Level, make reasonable efforts to remedy it, and adhere to the
response and resolution times set out in the following Incident Management table. The following
categories must be used when you log an Incident.
**Incident Management Table**
| Severity Level | Description |
| :------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 (Urgent) | Functionality in the production environment is unusable and is severely impacting all users and critical business functionality (and a workaround has not been defined). |
| 2 (High) | Functionality in the production environment is experiencing a partial failure or a significant performance degradation and a large subset of users or critical business functionality is impacted. |
| 3 (Normal) | A product issue has moderate or minor impact on a specific element of functionality in the production environment, or is not performing as expected in the production environment. The Service remains usable and the Incident is impacting a small number of users and has limited business impact. |
| 4 (Low) | Any Incident is affecting a Service that is not a Severity Level 1, 2 or 3. This may include a minor problem or enhancement request which may be cosmetic, or documentation-related issues. It may be an issue that would otherwise be a Severity Level 1, 2 or 3, but a very small percentage of users are impacted with little or no business impact, or workarounds are in place. |
Our standard target response and update times are outlined in the following Response Table. This
is based on business hours (9am to 5pm New Zealand Time) on Working Days.
**Response Table**
| Severity Level | First Response | Subsequent Updates |
| :------------: | :------------: | :----------------: |
| 1 | 4 Hours | 8 Hours |
| 2 | 8 Hours | 2 Working Days |
| 3 | 8 Hours | 5 Working Days |
| 4 | 1 Working Day | Best Efforts |
We may by written agreement offer and provide you with enhanced SLAs. Enhanced SLAs do not form
part of this SLA. If you require an enhanced SLA please contact us to discuss your requirements.
5. #### Exclusions [#exclusions]
The Service Commitment does not apply to any Unavailability, suspension or termination of the
Services, or any other performance issues related to the Services:
1. caused by factors outside of our reasonable control including any Force Majeure Event or
Internet access or related problems beyond the demarcation point of the Services;
2. that result from your actions or inactions or are caused by any third-party software or
infrastructure (e.g. third-party ledgers) you have chosen to use with the Services;
3. that result from your or a third-party's equipment, software, network or other technology
(other than third-party software and equipment within our direct control);
4. that result from you not following our reasonable directions or the best practices and
configuration described in MATTR Documentation;
5. occurring during planned maintenance; or
6. arising from our suspension or termination of your right to use the Services in accordance
with the Agreement.
If Availability is otherwise affected in a way that materially impacts you then we may, at our
discretion, issue a Service Credit to you.
For clarity, the Service Commitment does not apply to Preview Services, the Mobile Wallet
(including SDK and UI Reference Implementations), the Site, or the Self-Service Billing Portal.
6. #### Planned Maintenance [#planned-maintenance]
Our policy is to minimize down-time and planned maintenance impact on our customers. We will use
reasonable efforts to minimise the impact of situations where downtime is needed (for example,
planned system maintenance and specific release upgrades).
We will communicate planned system maintenance activity at least eight hours in advance
(typically seven days in advance). In very rare cases (for example, critical security patches)
we may need to undertake emergency maintenance without prior notification.
We aim to undertake planned system maintenance outside the core business hours for your Region.
Service Credits are not available for any Unavailability resulting from planned maintenance.
7. #### Release Management and Naming Conventions [#release-management-and-naming-conventions]
We use standard semantic versioning for our releases (X.Y.Z) as described in the following list:
1. **Major Change.** X represents a release with a “Major Change”. This could include, for
example, the introduction of a significant new feature, new version of an API or removal of
an API from the codebase (previously announced and marked as End of Life).
2. **Minor Change.** Y represents a “Minor Change”. This could include, for example, new
functionality added, changed, or marked as planned for retirement (while maintaining
backward compatibility).
3. **Patch Change.** Z represents a “Patch Change”. This is primarily used for the deployment
of fixes.
For each release, we will communicate what is new, what has been fixed, what has been improved,
what has been Retired and what has reached End of Life. We will make reasonable efforts to
deploy all releases in a manner that is as seamless to customers as is practical.
The day-to-day operation of our infrastructure to the Services is not restricted by the release
management conventions. We reserve the right to make changes outside of the release cycle in
order to maintain the health and availability of our Services.
8. #### Version and Feature Support [#version-and-feature-support]
We will always support one current version of our public cloud offering, and we will conduct
upgrades to that version.
When we introduce a new version of a feature or API, we will mark the existing version as
**"Retired"**. Retired product features and APIs are backwards compatible until they reach End
of Life, at which point they are no longer supported and will be removed from the platform.
We do not allow new customers to use Retired features or APIs. For Retired features and APIs, we
will not actively enhance them , and will only apply critical security fixes.
We will provide you with notice of our intent to remove a Retired feature or API from the
platform, at least 6 months prior to its removal (at this point, it is **"End of Life"**). The
End of Life notice may be provided at the same time as the feature or API is first marked as
Retired, or the feature or API may be Retired for a period before we notify its End of Life, in
which case it will continue to be retired for at least 6 months from that End of Life
notification.
We understand that the upgrade of features can be disruptive. If the proposed retirement
timeframe is problematic for you, you may notify us within 30 days after receiving the End of
Life notice, in which case, we will endeavour to offer you reasonable migration options.
9. #### Breaking Changes and API Backwards Compatibility [#breaking-changes-and-api-backwards-compatibility]
A **"breaking change"** is a change to our Services that may require you to change your
application for it to continue to work. Breaking changes include, for example, the removal or
change of intended functionality of an API endpoint, the removal or changing of the data type of
a request parameter, removing or changing the data type of a response property, or adding a
mandatory request parameter.
Changes that are not breaking changes include, for example, adding new API endpoints, optional
request parameters to existing API endpoints, the absence of a value or a default value (as
appropriate) that will maintain prior behavior, adding new response properties to existing API
endpoints, adding new values for existing parameters and properties that have enumerated sets of
values, changes to the order of response properties, changes to error messages, fixes to HTTP
response codes and error codes from incorrect code to correct code, and adding new optional HTTP
request or response headers. Resource and rate limits, and the default and maximum sizes of
paged data out of scope of the APIs, and may change.
An API is backwards compatible if an application written against one version of that API will
continue to work the same way, without modification, against future versions of the API.
10. #### Excluded Services [#excluded-services]
The following Services are not subject to the terms of this SLA. Service Credits and Service
Commitments are not available for these Services:
1. the Customer Portal;
2. the MATTR Site;
3. any Services supplied during a trial period; or
4. Services excluded from this SLA under applicable Service Terms, such as Third Party
Components.
11. #### Platform Protection [#platform-protection]
To ensure optimal experience for all our customers, our APIs are subject to rate limiting. These
limits help to mitigate against denial-of-service attacks, robots and any other form of
intended/unintended excessive load on the platform.
We understand that some customers may need to perform load tests against the platform. To ensure
we maintain quality of service for all our customers, if you need to undertake load testing
please contact us in advance so we can discuss how we can best address your needs.
Because penetration testing could interfere with the use of our Services by our other customers,
you must not perform or permit penetration testing without prior agreement.
We are also happy to share our internal testing and audit reports on request.
12. #### Security [#security]
For further information on our security policy and practices, including access, physical, data,
server and hosting security, please contact us.
13. #### Definitions [#definitions]
1. **Unavailability**: means no eligible requests to the Service can be executed due to a
Severity Level 1 defect.
2. **Availability**: means the Service is not experiencing Unavailability. Only Severity Level
1 Incidents impact availability.
3. **Incident**: means any Service-affecting issue having Severity Level 1, Severity Level 2,
Severity Level 3 or Severity Level 4, each as categorised in the Incident Management table.
4. **Force Majeure Event**: means any fact, matter or circumstance described in clause 19.4 of
the Customer Agreement document.
5. **Service Commitment**: has the definition set out in the paragraph headed ‘Service
Commitment’.
# MATTR Website Privacy Policy (archived)
URL: /docs/resources/terms/website-privacy-policy/20-7-21
This document is provided for archival purposes only.
Last Updated: 20 July 2021
MATTR is built and operated as a privacy-first company. Enhancing privacy and trust in digital
transactions is part of our DNA. This privacy policy explains how we collect, use and disclose
personal information and applies when you access or use our website, or if you are not representing
one of our customers and you communicate with us (e.g., through community forums), follow or
interact with us on social media, or take part in training and events (**Applicable Use**). We have
a separate privacy policy that applies when you access or use our services or materials on behalf of
one of our customers (e.g., our software, platforms and associated materials) (**Services**). This
can be accessed [here.](/docs/resources/terms/privacy-policy)
1. #### Who we are [#who-we-are]
When we say "**our**", "**we**", or "**us**", we mean MATTR Limited (**MATTR**). By accessing or
using our Site, you acknowledge that you have read and understood this privacy policy.
We provide easy-to use software, platforms and associated materials to improve trust and privacy
in digital interactions. As a New Zealand-based company, we comply with the New Zealand Privacy
Act (**Privacy Act**) when collecting, using or disclosing your personal information. This
policy does not limit or exclude any of your rights under the Privacy Act. You can find more
information about the Privacy Act and your rights
at [www.privacy.org.nz](https://www.privacy.org.nz/).
For all Applicable Use in the European Economic Area or in the United Kingdom, the section
titled “EEA users” applies to how we process your personal data.
2. #### How we collect personal information [#how-we-collect-personal-information]
When we say "**personal information**" we mean identifiable information about you. We may
collect personal information directly from you, for example, through your Applicable Use. We
only collect your personal information where we actually need it. For example, we may collect
your personal information when you use the Contact Us form on our website, which may ask you to
provide information like your name, email address, and organisation. In situations where
information does not identify you and from which your identity is not apparent (for example,
when we analyse website traffic based on IP addresses that we do not link with an identifiable
person) then that is not personal information and this privacy policy does not apply.
Here are some of the specific ways in which we may collect your personal information:
**Cookies**
A cookie is a small text file that websites can ask your browser to store on your device to
enhance your experience. We may use cookies for the purpose of storing preferences and
understanding how our website is used. We do not use any cookies for the purposes of marketing.
You can disable cookies by changing the settings on your browser, but this may impact your
experience of our website.
**Preventing bots and spam**
Our website is protected by the hCaptcha anti-bot service, which checks whether the data entered
on our website has been entered by a human or by an automated program. To learn more, you can
read
hCaptcha’s [privacy policy](https://www.hcaptcha.com/privacy) and [terms of service](https://www.hcaptcha.com/terms).
**Privacy-focused analytics**
We do not use Google Analytics. We’ve made the conscious choice to pay for a privacy-first
analytics tool called Matomo. To learn more, you can read about
[privacy-focused analytics](https://matomo.org/privacy/) and
[Matomo’s privacy policy](https://matomo.org/privacy-policy/).
3. #### How we use the personal information we collect [#how-we-use-the-personal-information-we-collect]
We use the personal information that we collect through your Applicable Use to:
* respond to you if you make an enquiry or apply for a job with us
* if you opt in, send you materials and updates about our business
* respond to you through community forums or social media
* provide, secure, update and maintain our website, social media accounts, training materials
and events (including providing applicable troubleshooting and support)
* analyse, improve and develop the functionality of our website, social media accounts, training
materials and events
* prevent, detect, or investigate security concerns, including fraud
* comply with applicable laws, regulations and legal processes, or to exercise, establish or
defend our legal rights, and
* fulfil any other request from you that is not captured above, or for any other purpose you
have consented to.
4. #### Disclosure of your personal information [#disclosure-of-your-personal-information]
We ***do not*** sell your personal information or share it without your consent. We will only
disclose your personal information:
* if required, and to the extent necessary, for the purpose the personal information was
collected (for example, we may reference you or your username (as applicable) when we respond
to you on a public community forum)
* if required, to comply with applicable laws, regulations, or legal processes, or to exercise,
establish or defend our legal rights
* to prevent, detect, or investigate security concerns, including fraud, and
* to the extent it is necessary, to trusted partners or service providers that we engage to
provide services to us or on our behalf who have agreed to protect your personal information
in a manner consistent with this privacy policy.
5. #### International Transfers of your personal information [#international-transfers-of-your-personal-information]
**All users**
When we do share your personal information, it may be transferred to, and processed in, a
country different to where you are located. These countries may have laws that are different to
what you are accustomed to. Where this is the case, we put comparable safeguards in place to
ensure your personal information remains protected.
**EEA Users**
For individuals in the European Economic Area or United Kingdom (**EEA**), or in relation to any
natural person who is identified or identifiable and in the EEA, this means that your data may
be transferred outside of the EEA in accordance with the
[MATTR Data Processing Terms](/docs/resources/terms/data-processing-terms). For further information,
please contact us using the details set out in the Contact us section below.
6. #### Protecting your personal information [#protecting-your-personal-information]
We will take reasonable steps to protect your personal information from loss, and unauthorised
access, use, modification and disclosure or other misuse.
If there is a privacy breach that it is reasonable to believe has caused serious harm to you or
is likely to do so, we will:
* notify you as soon as practicable after becoming aware of such a breach, and
* notify the New Zealand Privacy Commissioner or other regulatory authority as is required in
accordance with the Privacy Act or other applicable data protection and privacy laws.
Exceptions may apply if we are not legally required to provide a notice (for example, because we
believe that notice would endanger the safety of a person). We will take appropriate steps to
minimise the harm of the incident.
The security of your personal information is very important to us – but please remember that no
method of transmission over the Internet or method of electronic storage is 100% secure. While
we use commercially reasonable means to protect your personal information, we cannot guarantee
its absolute security.
7. #### Your rights [#your-rights]
You are entitled to access the personal information that we hold (provided it is readily
retrievable) and to request correction of your personal information. You may also ask us to
confirm whether we hold particular personal information. Your requests may be subject to certain
conditions or grounds for refusal, as set out in the Privacy Act.
If you want to exercise any of the above rights, or other rights you may have under applicable
data protection and privacy laws, please email us
at [privacy@mattr.global](mailto:privacy@mattr.global). You will need to confirm your identity
and set out the details of your request (e.g. the personal information you would like to access,
or the correction you are requesting).
8. #### How long we keep your personal information [#how-long-we-keep-your-personal-information]
We will retain your personal information only for as long as is necessary (for the purposes for
which it was collected) and as otherwise required by applicable legal or tax requirements.
9. #### Links to other websites [#links-to-other-websites]
If you follow a link from our website to a website hosted by a different entity, that website
will be subject to a different privacy policy and terms of service, which we recommend that you
review. The same applies when you are using a community forum, social media platform or the
electronic or physical resources of any agency other than MATTR.
10. #### Changes to this privacy policy [#changes-to-this-privacy-policy]
We may update this privacy policy from time to time by publishing the updated version on our
website. We will update the “Last Updated” date at the top of this page, and your Applicable Use
after that date will be subject to the updated policy.
11. #### Privacy queries and complaints [#privacy-queries-and-complaints]
We’re always keen to hear from you. If you’re curious about what personal information we hold
about you or have a question or feedback for us on this privacy policy, please contact our
Privacy and Data Protection Officer at [privacy@mattr.global](mailto:privacy@mattr.global).
If you’re not happy with how we are collecting, using or disclosing your personal information,
please let us know by [contacting us](mailto:privacy@mattr.global). We will review and
investigate your complaint and try to get back to you within a reasonable timeframe. You can
also request investigation by the New Zealand Privacy Commissioner at any time during or after
raising a complaint with us.
As a technology company, we prefer to communicate with you by email – this ensures that you’re
put in contact with the right person, in the right location, and in accordance with any
regulatory timeframes.
## Previous versions [#previous-versions]
1. [Privacy Policy - 25 March 2021 (archived)](/docs/resources/terms/website-privacy-policy/25-3-21)
# MATTR Website Privacy Policy (archived)
URL: /docs/resources/terms/website-privacy-policy/25-3-21
This document is provided for archival purposes only.
Last Updated: 25 March 2021
[See what's changed](/docs/resources/terms/website-privacy-policy/recent-changes) |
[Previous versions](#previous-versions)
MATTR is built and operated as a privacy-first company. Enhancing privacy and trust in digital
transactions is part of our DNA. This privacy policy explains how we collect, use and disclose
personal information and applies when you access or use our website, or if you are not representing
one of our customers and you communicate with us (e.g., through community forums), follow or
interact with us on social media, or take part in training and events (**Applicable Use**). We have
a separate privacy policy that applies when you access or use our services or materials on behalf of
one of our customers (e.g., our software, platforms and associated materials) (**Services**). This
can be accessed [here.](/docs/resources/terms/privacy-policy)
1. #### Who we are [#who-we-are]
When we say "**our**", "**we**", or "**us**", we mean MATTR Limited (**MATTR**). By accessing or
using our Site, you acknowledge that you have read and understood this privacy policy.
We provide easy-to use software, platforms and associated materials to improve trust and privacy
in digital interactions. As a New Zealand-based company, we comply with the New Zealand Privacy
Act (**Privacy Act**) when collecting, using or disclosing your personal information. This
policy does not limit or exclude any of your rights under the Privacy Act. You can find more
information about the Privacy Act and your rights
at [www.privacy.org.nz](http://www.privacy.org.nz).
For all Applicable Use in the European Economic Area or in the United Kingdom, the section
titled “EEA users” applies to how we process your personal data.
2. #### How we collect personal information [#how-we-collect-personal-information]
When we say "**personal information**" we mean identifiable information about you. We may
collect personal information directly from you, for example, through your Applicable Use. We
only collect your personal information where we actually need it. For example, we may collect
your personal information when you use the Contact Us form on our website, which may ask you to
provide information like your name, email address, and organisation. In situations where
information does not identify you and from which your identity is not apparent (for example,
when we analyse website traffic based on IP addresses that we do not link with an identifiable
person) then that is not personal information and this privacy policy does not apply.
Here are some of the specific ways in which we may collect your personal information:
**Cookies**
A cookie is a small text file that websites can ask your browser to store on your device to
enhance your experience. We may use cookies for the purpose of storing preferences. We do not
use any cookies for the purposes of marketing. You can disable cookies by changing the settings
on your browser, but this may impact your experience of our website.
**Preventing bots and spam**
Our website is protected by the hCaptcha anti-bot service, which checks whether the data entered
on our website has been entered by a human or by an automated program. To learn more, you can
read
hCaptcha’s [privacy policy](https://www.hcaptcha.com/privacy) and [terms of service](https://www.hcaptcha.com/terms).
**Privacy-focused analytics**
We do not use Google Analytics. We’ve made the conscious choice to pay for a privacy-first
analytics tool called Fathom. To learn more, you can read about
[privacy-focused analytics](https://usefathom.com/privacy-focused-web-analytics) and
[Fathom’s privacy policy](https://usefathom.com/privacy).
3. #### How we use the personal information we collect [#how-we-use-the-personal-information-we-collect]
We use the personal information that we collect through your Applicable Use to:
* respond to you if you make an enquiry or apply for a job with us
* if you opt in, send you materials and updates about our business
* respond to you through community forums or social media
* provide, secure, update and maintain our website, social media accounts, training materials
and events (including providing applicable troubleshooting and support)
* analyse, improve and develop the functionality of our website, social media accounts, training
materials and events
* prevent, detect, or investigate security concerns, including fraud
* comply with applicable laws, regulations and legal processes, or to exercise, establish or
defend our legal rights, and
* fulfil any other request from you that is not captured above, or for any other purpose you
have consented to.
4. #### Disclosure of your personal information [#disclosure-of-your-personal-information]
We ***do not*** sell your personal information or share it without your consent. We will only
disclose your personal information:
* if required, and to the extent necessary, for the purpose the personal information was
collected (for example, we may reference you or your username (as applicable) when we respond
to you on a public community forum)
* if required, to comply with applicable laws, regulations, or legal processes, or to exercise,
establish or defend our legal rights
* to prevent, detect, or investigate security concerns, including fraud, and
* to the extent it is necessary, to trusted partners or service providers that we engage to
provide services to us or on our behalf who have agreed to protect your personal information
in a manner consistent with this privacy policy.
5. #### International Transfers of your personal information [#international-transfers-of-your-personal-information]
**All users**
When we do share your personal information, it may be transferred to, and processed in, a
country different to where you are located. These countries may have laws that are different to
what you are accustomed to. Where this is the case, we put comparable safeguards in place to
ensure your personal information remains protected.
**EEA Users**
For individuals in the European Economic Area or United Kingdom (**EEA**), or in relation to any
natural person who is identified or identifiable and in the EEA, this means that your data may
be transferred outside of the EEA in accordance with the
[MATTR Data Processing Terms](/docs/resources/terms/data-processing-terms). For further information,
please contact us using the details set out in the Contact us section below.
6. #### Protecting your personal information [#protecting-your-personal-information]
We will take reasonable steps to protect your personal information from loss, and unauthorised
access, use, modification and disclosure or other misuse.
If there is a privacy breach that it is reasonable to believe has caused serious harm to you or
is likely to do so, we will:
* notify you as soon as practicable after becoming aware of such a breach, and
* notify the New Zealand Privacy Commissioner or other regulatory authority as is required in
accordance with the Privacy Act or other applicable data protection and privacy laws.
Exceptions may apply if we are not legally required to provide a notice (for example, because we
believe that notice would endanger the safety of a person). We will take appropriate steps to
minimise the harm of the incident.
The security of your personal information is very important to us – but please remember that no
method of transmission over the Internet or method of electronic storage is 100% secure. While
we use commercially reasonable means to protect your personal information, we cannot guarantee
its absolute security.
7. #### Your rights [#your-rights]
You are entitled to access the personal information that we hold (provided it is readily
retrievable) and to request correction of your personal information. You may also ask us to
confirm whether we hold particular personal information. Your requests may be subject to certain
conditions or grounds for refusal, as set out in the Privacy Act.
If you want to exercise any of the above rights, or other rights you may have under applicable
data protection and privacy laws, please email us
at [privacy@mattr.global](mailto:privacy@mattr.global). You will need to confirm your identity
and set out the details of your request (e.g. the personal information you would like to access,
or the correction you are requesting).
8. #### How long we keep your personal information [#how-long-we-keep-your-personal-information]
We will retain your personal information only for as long as is necessary (for the purposes for
which it was collected) and as otherwise required by applicable legal or tax requirements.
9. #### Links to other websites [#links-to-other-websites]
If you follow a link from our website to a website hosted by a different entity, that website
will be subject to a different privacy policy and terms of service, which we recommend that you
review. The same applies when you are using a community forum, social media platform or the
electronic or physical resources of any agency other than MATTR.
10. #### Changes to this privacy policy [#changes-to-this-privacy-policy]
We may update this privacy policy from time to time by publishing the updated version on our
website. We will update the “Last Updated” date at the top of this page, and your Applicable Use
after that date will be subject to the updated policy.
11. #### Privacy queries and complaints [#privacy-queries-and-complaints]
We’re always keen to hear from you. If you’re curious about what personal information we hold
about you or have a question or feedback for us on this privacy policy, please contact our
Privacy and Data Protection Officer at [privacy@mattr.global](mailto:privacy@mattr.global).
If you’re not happy with how we are collecting, using or disclosing your personal information,
please let us know by [contacting us](mailto:privacy@mattr.global). We will review and
investigate your complaint and try to get back to you within a reasonable timeframe. You can
also request investigation by the New Zealand Privacy Commissioner at any time during or after
raising a complaint with us.
As a technology company, we prefer to communicate with you by email – this ensures that you’re
put in contact with the right person, in the right location, and in accordance with any
regulatory timeframes.
# MATTR Website Privacy Policy (archived)
URL: /docs/resources/terms/website-privacy-policy/9-12-21
This document is provided for archival purposes only.
Last Updated: 9 December 2021
[See what's changed](/docs/resources/terms/website-privacy-policy/recent-changes) |
[Previous versions](#previous-versions)
MATTR is built and operated as a privacy-first company. Enhancing privacy and trust in digital
transactions is part of our DNA. This privacy policy explains how we collect, use and disclose
personal information and applies when you access or use our website, or if you are not representing
one of our customers and you communicate with us (e.g., through community forums or if you sign-up
to receive MATTR communications or content), follow or interact with us on social media, or take
part in training and events (**Applicable Use**). We have a separate privacy policy that applies
when you access or use our services or materials on behalf of one of our customers (e.g., our
software, platforms and associated materials) (**Services**). This can be accessed
[here.](/docs/resources/terms/privacy-policy)
1. #### Who we are [#who-we-are]
When we say "**our**", "**we**", or "**us**", we mean MATTR Limited (**MATTR**). By accessing or
using our Site, you acknowledge that you have read and understood this privacy policy.
We provide easy-to use software, platforms and associated materials to improve trust and privacy
in digital interactions. As a New Zealand-based company, we comply with the New Zealand Privacy
Act (**Privacy Act**) when collecting, using or disclosing your personal information. This
policy does not limit or exclude any of your rights under the Privacy Act. You can find more
information about the Privacy Act and your rights
at [www.privacy.org.nz](https://www.privacy.org.nz/).
For all Applicable Use in the European Economic Area or in the United Kingdom, the section
titled “EEA users” applies to how we process your personal data.
**How we collect personal information**
When we say "**personal information**" we mean identifiable information about you. We may
collect personal information directly from you, for example, through your Applicable Use. We
only collect your personal information where we actually need it. For example, we may collect
your personal information when you use the Contact Us form on our website, which may ask you to
provide information like your name, email address, and organisation. In situations where
information does not identify you and from which your identity is not apparent (for example,
when we analyse website traffic based on IP addresses that we do not link with an identifiable
person) then that is not personal information and this privacy policy does not apply.
Here are some of the specific ways in which we may collect your personal information:
**Cookies and similar technologies**
A cookie is a small text file that websites can ask your browser to store on your device to
enhance your experience. We may use cookies and other similar technologies (e.g., web beacons,
pixels or tags) for the purpose of storing preferences, understanding how our website is used,
and for the purposes of marketing (including to understand how you interact with our website and
marketing emails).
You can disable cookies by changing the settings on your browser, by not providing consent for
us to use cookies, or by changing your cookie preferences, but this may impact your experience
of our website.
***Pardot***
We use Pardot as a marketing automation tool, which uses first-party cookies for tracking
purposes and third-party cookies for redundancy (e.g., because of differences between how web
browsers use cookies). Pardot cookies do not store your personal information – only a unique
identifier.
Pardot tracks your activities on our website and on landing pages by setting first-party cookies
on your browsers if you provide consent for us to use cookies to track you or by using similar
technologies (e.g., pixels) in our marketing emails if you sign-up to MATTR communications and
content. These cookies and similar technologies are set in order to remember your preferences
(like form field values) when you return to our website and to help us track and understand how
you interact with our website and emails.
The following cookies are saved by Pardot/Salesforce for 3650 days:
* `visitor_id ` : **Visitor Cookie:** The visitor cookie includes the name
“visitor\_id” plus the unique identifier for MATTR, which is derived from the tracking code on
our website. The value stored is the unique ID for the visitor.
* `pi_opt_in` : **Opt-in Cookie:** The persistent cookie named “pi\_opt\_in” is used to
track a visitor. The value stored is “true” or “false”. If you do not opt-in to allow
cookies to track you, or if you ignore the opt-in banner, the Visitor Cookie is disabled and
you will not be tracked when you view our pages, forms, or landing pages, or download files or
click on a custom redirect link. In some circumstances, we may track you even if you do not
accept the Opt-in Cookie. For example, we may track you through form and landing page
submissions and when you send us an email, or open or click on our emails.
* `Visitor_id-hash` : This cookie is a security measure to make sure that a malicious
user can’t fake a being another visitor and access corresponding information.
* `lpv` : This LPV cookie is set to keep Pardot from tracking multiple page views on
a single asset over a 30-minute session. For example, if you reload a landing page several
times over a 30-minute period, this cookie keeps each reload from being tracked as a page
view.
* `pardot` : A session cookie named ‘pardot’ is set in your browser while you’re logged in as a
user or when you access a form, landing page, or page with Pardot tracking code. The cookie
indicates an active session and isn’t used for tracking.
***Google Ads***
We use the Google Ads platform to advertise and to understand the performance of our advertising
for marketing and sales purposes (**ad conversion data**). To enable this, if you provide
consent, we use cookies which may store information including:
* a unique identifier for you (as a visitor of our website); and
* a particular advertisement click that brought you to our website
* whether you take particular actions after clicking through from an advertisement, such as
submitting a sales inquiry.
This information is shared with Google for them to provide us with ad conversion data. If you
are otherwise identifiable by Google, for example because you are signed into a Google
application or service, you may be identifiable to them and us in connection with ad conversion
data related to you. You can read more about how Google uses information from websites like us
that use Google’s services [here](https://policies.google.com/technologies/partner-sites).
You can opt-out by visiting the Google Ad Setting website
[here](https://adssettings.google.com/authenticated). To learn more about Google’s privacy
practices you can read Google’s Privacy Policy and Terms website
[here](https://policies.google.com/privacy).
**Preventing bots and spam**
Our website is protected by the hCaptcha anti-bot service, which checks whether the data entered
on our website has been entered by a human or by an automated program. To learn more, you can
read
hCaptcha’s [privacy policy](https://www.hcaptcha.com/privacy) and [terms of service](https://www.hcaptcha.com/terms).
**Privacy-focused analytics**
In addition to Pardot and Google Ads, which is described in the Cookies section above, we’ve
made the conscious choice to pay for a privacy-first analytics tool called Matomo. To learn
more, you can read
about [privacy-focused analytics](https://matomo.org/privacy/) and [Matomo’s privacy policy](https://matomo.org/privacy-policy/).
2. #### How we use the personal information we collect [#how-we-use-the-personal-information-we-collect]
We use the personal information that we collect through your Applicable Use to:
* respond to you if you make an enquiry or apply for a job with us
* if you opt in, for the purposes of marketing or to send you materials and updates about our
business (including by tracking your use of, and interaction with, our website and marketing
emails)
* respond to you through community forums or social media
* provide, secure, update and maintain our website, social media accounts, training materials
and events (including providing applicable troubleshooting and support)
* analyse, improve and develop the functionality of our website, social media accounts, training
materials and events
* prevent, detect, or investigate security concerns, including fraud
* comply with applicable laws, regulations and legal processes, demonstrate such compliance, or
to exercise, establish or defend our legal rights, and
* fulfil any other request from you that is not captured above, or for any other purpose you
have consented to.
**Disclosure of your personal information**
We ***do not*** sell your personal information or share it without your consent. We will only
disclose your personal information:
* if required, and to the extent necessary, for the purpose the personal information was
collected (for example, we may reference you or your username (as applicable) when we respond
to you on a public community forum)
* if required, to comply with applicable laws, regulations, or legal processes, demonstrate such
compliance, or to exercise, establish or defend our legal rights
* to prevent, detect, or investigate security concerns, including fraud, and
* to the extent it is necessary, to trusted partners or service providers that we engage to
provide services to us or on our behalf who have agreed to protect your personal information
in a manner consistent with this privacy policy.
**International Transfers of your personal information**
**All users**
When we do share your personal information, it may be transferred to, and processed in, a
country different to where you are located. These countries may have laws that are different to
what you are accustomed to. Where this is the case, we put comparable safeguards in place to
ensure your personal information remains protected.
**EEA Users**
For individuals in the European Economic Area or United Kingdom (**EEA**), or in relation to any
natural person who is identified or identifiable and in the EEA, this means that your data may
be transferred outside of the EEA in accordance with
the [MATTR Data Processing Terms](https://learn.mattr.global/docs/resources/terms/data-processing-terms).
For further information, please contact us using the details set out in the Contact us section
below.
**Protecting your personal information**
We will take reasonable steps to protect your personal information from loss, and unauthorised
access, use, modification and disclosure or other misuse.
If there is a privacy breach that it is reasonable to believe has caused serious harm to you or
is likely to do so, we will:
* notify you as soon as practicable after becoming aware of such a breach, and
* notify the New Zealand Privacy Commissioner or other regulatory authority as is required in
accordance with the Privacy Act or other applicable data protection and privacy laws.
Exceptions may apply if we are not legally required to provide a notice (for example, because we
believe that notice would endanger the safety of a person). We will take appropriate steps to
minimise the harm of the incident.
The security of your personal information is very important to us – but please remember that no
method of transmission over the Internet or method of electronic storage is 100% secure. While
we use commercially reasonable means to protect your personal information, we cannot guarantee
its absolute security.
**Your rights**
You are entitled to access the personal information that we hold (provided it is readily
retrievable) and to request correction of your personal information. You may also ask us to
confirm whether we hold particular personal information. Your requests may be subject to certain
conditions or grounds for refusal, as set out in the Privacy Act.
If you want to exercise any of the above rights, or other rights you may have under applicable
data protection and privacy laws, please email us
at [privacy@mattr.global](mailto:privacy@mattr.global). You will need to confirm your identity
and set out the details of your request (e.g. the personal information you would like to access,
or the correction you are requesting).
**How long we keep your personal information**
We will retain your personal information only for as long as is necessary (for the purposes for
which it was collected) and as otherwise required by applicable legal or tax requirements.
**Links to other websites**
If you follow a link from our website to a website hosted by a different entity, that website
will be subject to a different privacy policy and terms of service, which we recommend that you
review. The same applies when you are using a community forum, social media platform or the
electronic or physical resources of any agency other than MATTR.
**Changes to this privacy policy**
We may update this privacy policy from time to time by publishing the updated version on our
website. We will update the “Last Updated” date at the top of this page, and your Applicable Use
after that date will be subject to the updated policy.
**Privacy queries and complaints**
We’re always keen to hear from you. If you’re curious about what personal information we hold
about you or have a question or feedback for us on this privacy policy, please contact our
Privacy and Data Protection Officer at [privacy@mattr.global](mailto:privacy@mattr.global).
If you’re not happy with how we are collecting, using or disclosing your personal information,
please let us know by [contacting us](mailto:privacy@mattr.global). We will review and
investigate your complaint and try to get back to you within a reasonable timeframe. You can
also request investigation by the New Zealand Privacy Commissioner at any time during or after
raising a complaint with us.
As a technology company, we prefer to communicate with you by email – this ensures that you’re
put in contact with the right person, in the right location, and in accordance with any
regulatory timeframes.
## Previous versions [#previous-versions]
1. [Privacy Policy - 20 July 2021 (archived)](/docs/resources/terms/website-privacy-policy/20-7-21)
2. [Privacy Policy - 25 March 2021 (archived)](/docs/resources/terms/website-privacy-policy/25-3-21)
# Website Privacy Policy - What's Changed
URL: /docs/resources/terms/website-privacy-policy/recent-changes
By using the Website after the “Last updated” date at the top of our
[Privacy Policy](/docs/resources/terms/privacy-policy), you are deemed to have accepted the updated Terms.
## Changes posted December 9, 2021 [#changes-posted-december-9-2021]
MATTR updated the [Website Privacy Policy](/docs/resources/terms/website-privacy-policy/9-12-21) on December, 9 2021
* Inclusion of first-party cookies tracking with Pardot and Google Ads.
## Changes posted July 20, 2021 [#changes-posted-july-20-2021]
MATTR updated the [Website Privacy Policy](/docs/resources/terms/website-privacy-policy/20-7-21) on July 20, 2021.
* Changed web analytics solution from Fathom to Matomo.
## Changes posted March 25, 2021 [#changes-posted-march-25-2021]
MATTR updated the [Website Privacy Policy](/docs/resources/terms/website-privacy-policy/25-3-21) on March 25, 2021.
* The Website Privacy Policy was published.
# Website Terms of Use
URL: /docs/resources/terms/website-terms-of-use
Last Updated: 25 March 2021
[See what's changed](/docs/resources/terms/website-terms-of-use/recent-changes) |
[Previous versions](#previous-versions)
## Welcome to the MATTR website! [#welcome-to-the-mattr-website]
These Website Terms of Use (Terms) are agreed between MATTR Limited (**we**, **us**, or **our**) and
you or the entity you represent, as applicable (**you** or **your**) and apply only when you access
or use our Site (**Website**). Different terms apply when you access or use our Services (e.g., our
software, platforms and associated materials) (**Services**). These other terms can be accessed
[here](/docs/resources/terms/customer-agreement).
By accessing or using our Website, you acknowledge that you have read and accepted these Terms. We
may update or replace these Terms from time to time by publishing a new version on our Website. By
using the Website after the “Last updated” date at the top of these Terms, you are deemed to have
accepted the updated Terms. If you don’t agree to the updated Terms, you must stop accessing and
using the Website.
1. #### Your access or use of our Website [#your-access-or-use-of-our-website]
The information provided on our Website is intended only as an introduction and guide to our
range of available Services. We use reasonable endeavours to keep the information on the Website
accurate and up-to-date but we do not guarantee the accuracy, currency or completeness of the
information provided.
The content provided by us on the Website is owned or controlled by us and is protected by
copyright laws In New Zealand and overseas. See "Intellectual Property" below for more
information.
You must not use our Website or copy, reproduce, modify or transmit its contents:
* other than in accordance with these Terms or for commercial purposes without our prior written
permission;
* in breach of any law or for any unlawful act; or
* to damage or disrupt our Website, Services or any other service or website.
You may be required to provide us with information, for example, when you register for a Service
or contact us. This may involve providing us with your personal information. You agree that any
such information provided is truthful and up-to-date. Any personal information provided to us
through your use of our Website will be collected and handled in accordance with our
[Website Privacy Policy](/docs/resources/terms/website-privacy-policy).
2. #### Liability [#liability]
We accept no responsibility for errors in the content on this Website at any time. We do not
guarantee that access to, or use of, our Website will be uninterrupted or error-free.
To the maximum extent permitted by law:
* our Website and the information presented on it is provided on an “as-is” and “as-available”
basis without any warranties, representations, or guarantees of any kind (whether, express,
implied, statutory or otherwise) including, but not limited to, warranties of
non-infringement, merchantability, or fitness for a particular purpose; and
* neither we nor our suppliers will be liable under the law of tort (including negligence),
contract or otherwise for any loss of income, profits, data, business opportunity or savings
or for any indirect, incidental, consequential, exemplary, punitive or special loss or damage
of any person, however caused, arising out of or in connection with your access to or use of
our Website or reliance on any information or content on our Website.
In some places, like New Zealand and Australia, there may be non-excludable warranties,
guarantees or other rights provided by law. They still apply – these terms do not exclude,
restrict or modify them. Our liability for breach of such non-excludable consumer guarantee is
limited, at our option, to re-performing the relevant service (unless the non-excludable
consumer guarantee says otherwise).
3. #### Intellectual property [#intellectual-property]
We or our licensors (as applicable) own all copyright and all other intellectual property rights
that may subsist in this Website or any content presented on this Website (and we reserve all
rights in relation to such material). You may use this Website for non-commercial purposes only,
and you must:
* not remove any copyright, trademark and other proprietary notices contained in the content;
* not copy, reproduce, display, modify, redistribute, transmit, sub-license or otherwise use for
commercial purposes any portion of this Website without our prior written permission; and
* if we provide such permission, include an attribution to MATTR Limited if you use any
information contained on our Website.
The trademarks appearing on our Website belong to us, our suppliers, or our licensors as
applicable and are protected by law. You must not use or reproduce or allow anyone to use or
reproduce those trademarks for any reason without prior written permission.
4. #### Feedback and unsolicited submissions [#feedback-and-unsolicited-submissions]
If you give us feedback or submit ideas about our Website or Services (**Ideas**), you:
* grant us the right to use, modify or develop those Ideas or variations on them to improve our
Website and our Services (and for any other purpose we deem necessary or desirable) without
being obliged to seek permission or provide payment or any other compensation in respect of
our use of those Ideas and without any other restriction;
* waive any moral rights in those Ideas and consent to us freely using those Ideas without
further consultation, consent or attribution to you;
* irrevocably assign to us all right, title, and interest in and to those Ideas and agree to
provide us any assistance we require to document, perfect, and maintain our rights in those
Ideas; and
* warrant that you have all necessary rights and titles grant us the right, title and interest
described here.
5. #### Third party sites [#third-party-sites]
Our Website may include links to external sites. These sites are not under our control and we
are not responsible for, and we make no representations or warranties concerning the contents of
any such external site. Such links are provided to you only as a convenience, and the inclusion
of any link does not imply any recommendation, endorsement, verification, or certification by us
of the linked site.
6. #### Jurisdiction [#jurisdiction]
Our Website and these Terms are governed by the laws of New Zealand and the courts of New
Zealand will have non-exclusive jurisdiction to hear and determine any dispute arising in
relation to them. You must not object to the transfer of any proceedings to New Zealand courts
on any basis, including inconvenience.
# Website Terms of Use - What's Changed
URL: /docs/resources/terms/website-terms-of-use/recent-changes
## Changes posted March 25, 2021 [#changes-posted-march-25-2021]
MATTR updated the [Website Terms of Use](/docs/resources/terms/website-terms-of-use) on March 25, 2021.
The [Website Terms of Use](/docs/resources/terms/website-terms-of-use) were published.
By using the Website after the “Last updated” date at the top of these Terms, you are deemed to have accepted the updated Terms.
# Android Holder SDK v6.0.0 Migration Guide
URL: /docs/holding/sdk-operations/migration-guides/android-6.0.0-migration-guide
## Overview [#overview]
This guide provides a comprehensive overview of the changes introduced in MobileCredentialHolderSDK v6.0.0 for Android, including breaking changes, new features, and migration steps.
## Key Features [#key-features]
* **Seamless, OS-native credential presentation (DC API support)**: The Android Holder SDK now integrates with the Digital Credentials API, allowing credentials to be presented via an OS overlay without launching your holder app. The OS automatically surfaces only matching credentials across installed wallets — reducing friction, avoiding dead ends, and significantly improving the user experience.
* **Clearer OID4VCI interoperability (v1.0 alignment)**: The Android Holder SDK now aligns with the finalized OID4VCI v1.0 specification (upgraded from draft-12). This alignment improves interoperability across the ecosystem, enabling other systems to clearly identify your supported version and feature set, ensuring smoother integrations and consistent behavior across platforms.
* **Improved reliability in contactless flows**: Enhanced BLE performance delivers more consistent proximity credential exchanges and faster engagements.
* **Stronger cryptography and standards alignment**: Updated COSE algorithms (as per RFC 9864) strengthen cryptographic compatibility and ensure continued compliance with evolving standards.
* **Simpler, Decoupled Releases**: The Android Holder SDK no longer has a shared common module. This allows us to decouple the releases of Holder and Verifier and versions no longer need to match.
* **Improved UI in NFC presentations**: The SDK now provides enhanced handling of system UI prompts on devices that support multiple NFC-enabled mobile credential wallets, ensuring clearer wallet selection and a smoother presentation flow.
* **General stability and performance improvements**: Multiple refinements reduce integration friction, increase consistency across mobile environments, and improve overall user experience.
For a detailed list of changes included in this release, refer to the [SDK Changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/).
## Breaking Changes [#breaking-changes]
This section outlines the breaking changes introduced in v6.0.0 that require updates to your existing implementation:
| # | Element | Change | Impact |
| - | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| 1 | `MobileCredentialHolder.getCredential(credentialId,skipStatusCheck = ...)` | Parameter renamed to `fetchUpdatedStatusList = ...` with **inverted semantics** | All call sites using `skipStatusCheck = ...` must be updated. |
| 2 | `OfferedCredential` | New required property `credentialConfigurationId: String` | Manual decoders must handle new field. |
| 3 | `VerifierAuthenticationResult` | New subclass `Unsigned(val origin: String?)` | Exhaustive `when` statements must add new case. |
| 4 | `MobileCredentialRequest.verifierAuthenticationResult` | No longer nullable | Exhaustive `when` statements, or null checks, must now handle `Unsigned` in place of `null`. |
| 5 | `global.mattr.mobilecredential.common` | Renamed `global.mattr.mobilecredential.holder` | All uses of any `common` must be updated to use `holder`. |
| 6 | `MobileCredentialHolder.NFC` | NFC Activity is now launched automatically by the SDK | Calls to `MobileCredentialHolder.Nfc.setDeviceEngagementListener` must be updated to align with the new automatic Activity handling. |
## New Additions [#new-additions]
### Types [#types]
| Type | Purpose |
| --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `DcmConfiguration` | Configure Digital Credentials support (enable/disable) |
| `MobileCredentialHolder.Nfc.onActivityResume` | Notifies Android that your `Activity` should be treated as the preferred NFC handler while it is in the foreground. |
| `MobileCredentialHolder.Nfc.onActivityPause` | Notifies Android that your `Activity` should no longer be treated as the preferred NFC handler when it leaves the foreground. |
### Sealed & Enum Classes [#sealed--enum-classes]
| Type | New Case |
| ------------------------------ | ------------------------------- |
| `VerifierAuthenticationResult` | `Unsigned(val origin: String?)` |
### New Dependencies [#new-dependencies]
* `androidx.credentials.registry:registry-provider`
* `androidx.credentials.registry:registry-provider-play-services`
## Bug Fixes [#bug-fixes]
* Fixed parsing of status lists that use a bit size other than 2.
* Resolved BLE connection stalls on Pixel 7 Pro devices.
* Resolved NFC engagement issues on Pixel 6 Pro devices.
* Aligned WebView OID4VCI error messages for improved consistency.
## Minimum Requirements [#minimum-requirements]
* Android 7 / Nougat / API 24.
* The Android Holder SDK is built using Kotlin 2.0. This adds some intrinsic dependencies into your build tools.
* Kotlin 2.0 is supported from AGP version [8.5](https://developer.android.com/build/kotlin-support).
* AGP 8.5 is supported from Gradle version [8.7](https://developer.android.com/build/releases/about-agp) and Android Studio Koala [2024.1.1](https://developer.android.com/studio/releases).
## Migration Steps [#migration-steps]
### Rename any references to the `common` dependency to `holder` [#rename-any-references-to-the-common-dependency-to-holder]
The shared `common` module has been removed and the Android Holder SDK is now published as `holder`. This means that any references to `global.mattr.mobilecredential.common` in your codebase should be updated to `global.mattr.mobilecredential.holder`.
This can be done with a global find and replace across your codebase. If you are using both the Holder and Verifier SDKs, you will need to limit your search to the relevant parts of your application.
### Update `getCredential` calls [#update-getcredential-calls]
The `skipStatusCheck` parameter has been renamed to `fetchUpdatedStatusList` with inverted semantics. When `fetchUpdatedStatusList` is `true` (default), the SDK will fetch the latest status list to ensure up-to-date revocation information. When `false`, it will skip fetching the status list and rely on cached data (when valid). Update all calls accordingly:
```diff
- val credential = holder.getCredential(credentialId = id, skipStatusCheck = true)
+ val credential = holder.getCredential(credentialId = id, fetchUpdatedStatusList = false)
```
| Old Parameter | New Parameter |
| ----------------------------------- | ----------------------------------------- |
| `skipStatusCheck = false` (default) | `fetchUpdatedStatusList = true` (default) |
| `skipStatusCheck = true` | `fetchUpdatedStatusList = false` |
Refer to [Revocation Status check](/docs/holding/credential-claiming-guides/revocation-status-check) for more information.
### Handle `OfferedCredential` changes [#handle-offeredcredential-changes]
The `OfferedCredential` data class now includes a new required property `val credentialConfigurationId: String`. If you have custom encoding or decoding logic for this object, make sure it is updated to handle the new field:
```diff
data class OfferedCredential(
val doctype: String,
val claims: List,
val name: String?,
+ val credentialConfigurationId: String,
internal val scope: String
)
```
### Handle `VerifierAuthenticationResult` changes [#handle-verifierauthenticationresult-changes]
The `VerifierAuthenticationResult` sealed class now includes a new subclass `Unsigned(val origin: String?)` to represent unsigned/unauthenticated requests. If you have any exhaustive `when` statements on `VerifierAuthenticationResult`, you must add a new case to handle this scenario:
```diff
val trusted = when (verifierAuthenticationResult) {
is Trusted ->
"Trusted verifier"
is Untrusted ->
"Authentication failed")
- else ->
+ is Unsigned ->
"No authentication"
}
```
### Handle NFC device engagement changes [#handle-nfc-device-engagement-changes]
Starting with Android Holder SDK v6.0.0, the SDK automatically launches an Activity when NFC device engagement occurs. Manual Activity launching from your NFC engagement listener is no longer required.
To migrate your implementation, make the following changes:
1. In your AndroidManifest.xml, add the following intent-filter to the Activity that should be started (or brought to the foreground) when NFC device engagement begins:
```xml
```
Any Activity that declares this intent filter will be automatically launched by the SDK when NFC device engagement starts.
2. Remove any manual Activity launching logic from your NFC engagement listener.
3. Your NFC device engagement listener should now only handle UI updates (for example, displaying your NFC screen). It should no longer start an `Activity`. It is recommended to register the listener in the NFC Activity’s `onCreate()` method (or in a related `ViewModel`), rather than in your `Application` class:
```diff
-class YourApplication : Application() {
+class NfcActivity : Activity() {
override fun onCreate() {
super.onCreate()
MobileCredentialHolder.Nfc.setDeviceEngagementListener {
- startNfcActivity()
+ showNfcUI()
}
}
}
```
4. To improve the user experience on devices with multiple NFC-capable mobile credential wallets, you can **optionally** explicitly mark your `Activity` as the preferred NFC handler while it is in the foreground:
```kotlin
override fun onResume() {
super.onResume()
MobileCredentialHolder.Nfc.onActivityResume(this)
}
override fun onPause() {
MobileCredentialHolder.Nfc.onActivityPause(this)
super.onPause()
}
```
This ensures your `Activity` is prioritised for NFC handling while visible.
### Enable Presenting Credentials via the Digital Credentials API (Optional) [#enable-presenting-credentials-via-the-digital-credentials-api-optional]
The Android Holder SDK v6.0.0 introduces support for the Digital Credentials API, allowing credentials to be presented via an OS-native overlay without launching your app. To enable this feature, you need to initialize the SDK with a `DcmConfiguration` that sets enabled to `true`. This is optional but recommended for a seamless user experience.
```diff
MobileCredentialHolder.getInstance().initialize(
// ...
+ dcmConfiguration = DcmConfiguration(enabled = true)
)
```
### Enable Confirmation and Error Handling for the Digital Credentials API (Optional) [#enable-confirmation-and-error-handling-for-the-digital-credentials-api-optional]
The Digital Credentials API support includes new intents for handling confirmation and error scenarios during credential presentation. To enable this, you need to add intent filters to your AndroidManifest.xml and handle the incoming intents in your activity:
```xml
```
```kotlin
val verifierAuthenticationResult =
IntentCompat.getParcelableExtra(
intent,
"global.mattr.credentialmanager.extra.verifier_authentication_result",
VerifierAuthenticationResult::class.java
)
```
```xml
```
# Android Holder SDK v7.0.0 Migration Guide
URL: /docs/holding/sdk-operations/migration-guides/android-7.0.0-migration-guide
Description: A comprehensive guide to migrating to Android Holder SDK v7.0.0, covering breaking changes, new features, and step-by-step migration instructions.
## Overview [#overview]
This guide provides a comprehensive overview of the changes introduced in the Android Holder SDK
v7.0.0, including breaking changes, new features, and migration steps.
This release focuses on strengthening trust at issuance, improving predictability in credential
handling, and increasing consistency across platforms. It introduces optional SDK Tethering to
connect the SDK to your MATTR VII tenant, adds optional Wallet Attestation support so issuers can
verify wallet integrity before issuing credentials, and strengthens validation and resilience in
credential flows.
SDK Tethering (and Wallet Attestation, which builds on it) is **optional** in this release. You opt
in by passing a `platformConfiguration` when initializing the SDK. If you omit it, the SDK skips
registration, tethering, and wallet attestation, and existing integrations continue to work. We
expect to make SDK Tethering required in an upcoming release, so we recommend adopting it now to
prepare.
## Key Features [#key-features]
* **SDK Tethering (optional)**: The Android Holder SDK can now be tethered to a MATTR VII tenant,
tying each SDK/app instance to your tenant. This allows you to view details about registered and
active app instances directly from your tenant for operational insights. SDK Tethering also
establishes a remote management channel that we expect to extend in the future with additional
features, such as remote syncing of trusted issuer lists and eventing. Tethering is enabled by
providing a `platformConfiguration` at initialization.
* **Wallet Attestation support (optional)**: Building on the SDK Tethering channel, the Android
Holder SDK now supports Wallet Attestation, allowing your holder application to prove to issuers
that it is a trusted wallet before credentials are issued. When an issuer requires it, the SDK
uses the tethering connection to obtain wallet attestation tokens from your MATTR VII tenant.
Wallet Attestation requires SDK Tethering to be configured.
* **Stronger application identity during issuance**: Pre-authorized credential issuance flows now
pass the application's `client_id`, ensuring the holder is accurately represented when interacting
with issuers. This improves compatibility with issuers applying stricter controls.
* **More predictable credential retrieval results**: Credential retrieval responses are now more
structured and deterministic, explicitly identifying success or failure with guaranteed fields per
result type.
* **Stronger validation in presentation flows**: Stricter JWT request validation, state size
validation, and certificate chain validation improve how the SDK handles malformed or incomplete
requests.
* **Improved resilience in issuance flows**: Oversized credential payload validation prevents
failure modes that could lead to degraded user experiences.
* **General stability and performance improvements**: Multiple refinements reduce integration
friction, increase consistency, and improve overall reliability.
## Breaking Changes [#breaking-changes]
This section outlines the breaking changes introduced in v7.0.0 that require updates to your
existing implementation:
| # | Change | Impact |
| -- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | `UserAuthenticationConfiguration` now takes a `userAuthenticationType` parameter | Configure biometric authentication behavior. `initialize` may throw `HolderException.UserAuthenticationNotSupportedException` if you use `BiometryCurrentSet` with `OnInitialize`. |
| 2 | Pre-authorized code flow now uses the application's `client_id` instead of a default identifier | Ensure your application has a valid configured `client_id` and issuers recognize it. |
| 3 | Credential retrieval result shape updated to explicitly identify success or failure with guaranteed fields | Update result parsing logic to use success/failure branching. |
| 4 | `OfferedCredential.doctype` renamed to `docType` | Rename all usages of `doctype` to `docType`. |
| 5 | `AuthenticationOption` renamed to `DeviceAuthenticationOption` (and the `authenticationOption` parameter on `createProximityPresentationSession` renamed to `deviceAuthenticationOption`) | Update all imports, type references, and the session-creation argument name. |
| 6 | `OfferedCredential.claims` is now optional | Handle the case where `claims` is `null`, indicating no claim data is included in the offer. |
| 7 | `RetrieveCredentialResult` is now a sealed interface with `Success` and `Failure` variants | Replace field-based branching with `when`/`is` pattern matching. |
| 8 | `OnlinePresentationSession.matchedCredentials` is now the `getMatchedCredentials()` method | Replace property access with the method call. |
| 9 | `sessionStatus` parameter removed from `ProximityPresentationSession.terminateSession` | Listen for `SessionStatus.SessionTerminated` in your presentation session callback instead of passing a custom status. |
| 10 | Required `authorizationServerIssuer` and `tokenEndpointAuthMethodsSupported` fields added to `DiscoveredCredentialOffer` | Supply the new arguments if you construct this type directly. |
| 11 | `ConnectivityError` and `InvalidWalletAttestation` added to `RetrieveCredentialError` | Handle the new cases in exhaustive `when` statements. |
| 12 | Stricter JWT request validation for online presentations: enforces expected `typ`, validates `iat`, rejects expired `exp` | Where possible, ensure verifiers generate compliant JWT authorization request objects. |
| 13 | Oversized `state` values in VP online presentation requests are now rejected | Where possible, ensure verifier-generated `state` values remain within supported limits. |
| 14 | Oversized credential fields in pre-authorized issuance are now rejected | Where possible, validate issuer payload sizes and test with realistic credential data. |
| 15 | `mdocIacasUri` is now optional | Review assumptions that this field is always present. |
## Migration Steps [#migration-steps]
### Configure `userAuthenticationType` [#configure-userauthenticationtype]
`UserAuthenticationConfiguration` now takes a `userAuthenticationType` parameter to configure
biometric authentication behavior, aligning with iOS. The default authentication type is
`UserPresence`. Review your configuration and note that
`initialize` may now throw `HolderException.UserAuthenticationNotSupportedException` if you attempt
to use `BiometryCurrentSet` with `OnInitialize`:
```diff
val userAuthConfig = UserAuthenticationConfiguration(
userAuthenticationBehavior = OnDeviceKeyAccess,
+ userAuthenticationType = UserPresence
)
```
`UserPresence` matches the implicit behavior of v6.x, so existing integrations are unaffected if you
keep it. Opt in to `BiometryCurrentSet` only if you require that stricter behavior.
### (Optional) Create a holder application on your MATTR VII tenant [#optional-create-a-holder-application-on-your-mattr-vii-tenant]
SDK Tethering is **optional** in this release. If you do not need it, you can skip this step and the
next one. The SDK continues to function without a `platformConfiguration`.
To enable SDK Tethering, the Android Holder SDK connects to a MATTR VII tenant. Tethering gives you
access to a centralized view of registered and active app instances in MATTR VII, the optional
Wallet Attestation feature, and other centralized management capabilities we plan to deliver over
this channel.
We expect SDK Tethering to become required in an upcoming release. We recommend adopting it now to
prepare, even if you do not yet use Wallet Attestation.
To register your Android application, make a request to create a holder application:
```http title="Request"
POST /v1/holder/applications
```
```json title="Request body"
{
"name": "My Android Holder Application",
"clientId": "your-wallet-client-id",
"type": "android",
"packageName": "com.yourcompany.holderapp",
"packageSigningCertificateThumbprints": [
"1232584b6f6a892d356899fb9576c5f226a179e6199f2b7a1d837b5c234c5a8e"
]
}
```
* `name`: A unique name to identify your holder application.
* `clientId`: The OAuth 2.0 `client_id` that identifies your wallet application. This value is
included in attestation JWTs and must match the `client_id` configured on the issuer's
Authorization Server.
* `type`: Must be `android` for an Android application.
* `packageName`: The package name of your Android application.
* `packageSigningCertificateThumbprints`: SHA-256 hex-encoded fingerprints of the signing key
certificates used to sign your APK or app bundle. This ensures the tenant only accepts requests
from known and trusted applications. Refer to [Android app signing](/docs/holding/android-app-signing)
for more information.
Once created, your holder application will be able to use the SDK and interact with the MATTR VII
platform (for example, to obtain attestation tokens).
The response will include a unique `id` for your application, which must be used when initializing
the SDK so that it can correctly identify and authenticate your application.
The `clientId` you configure here is the same value you must:
1. Pass as the `clientId` parameter when calling [`retrieveCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/retrieve-credentials.html).
2. Register with each issuer you intend to interact with, so the issuer can identify and trust
requests coming from your wallet application.
### (Optional) Enable tethering at SDK initialization [#optional-enable-tethering-at-sdk-initialization]
To enable SDK Tethering, pass a `platformConfiguration` when initializing the SDK. This parameter is
optional. Omit it to initialize the SDK without tethering:
```diff
- MobileCredentialHolder.initialize(context, instanceId)
+ val platformConfig = PlatformConfiguration(
+ tenantHost = URL("https://your-tenant.vii.mattr.global"),
+ applicationId = "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
+ )
+ MobileCredentialHolder.initialize(context, instanceId, platformConfiguration = platformConfig)
```
* `tenantHost`: The URL of your MATTR VII tenant where your holder application is configured.
* `applicationId`: The `id` of your configured MATTR VII holder application.
When `platformConfiguration` is omitted, the SDK skips registration, tethering, and wallet
attestation.
### Handle new error cases [#handle-new-error-cases]
This release adds two new values to `RetrieveCredentialError`, `ConnectivityError` and
`InvalidWalletAttestation`. This is a **breaking change** that requires updates to exhaustive `when`
blocks over this enum regardless of whether you enable SDK Tethering.
If you enable SDK Tethering and Wallet Attestation, the following exceptions on `HolderException`
also become relevant during credential issuance:
* `HolderException.InvalidCredentialOfferException`: The offer requires attestation but no
`platformConfiguration` was provided, or the SDK does not support any of the client authentication
methods advertised by the authorization server.
* `HolderException.WalletAttestationFailedException`: The SDK could not obtain an attestation token
from the MATTR VII tenant.
* `HolderException.InvalidWalletAttestationException`: The authorization server rejected the
attestation token.
The [`retrieveCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/retrieve-credentials.html)
method may now throw these exceptions. Wallet attestation errors only occur when the SDK is tethered
and an issuer requires attestation.
Update your error handling, logging, analytics, and support diagnostics to account for these new
error cases.
### Update `client_id` configuration [#update-client_id-configuration]
Previously, the `client_id` passed to
[`retrieveCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/retrieve-credentials.html)
was not shared with the issuer during the pre-authorized code flow, so any value would work. This is
no longer the case. The SDK now presents the `client_id` to the issuer as part of wallet
attestation, and the issuer validates it against its list of trusted wallet providers.
To prepare for this change:
1. **Coordinate with the issuer** to register your wallet application as a trusted wallet provider.
The issuer will provide you with a `client_id` that identifies your application.
2. **Pass the issuer-provided `client_id`** when calling `retrieveCredentials`.
```diff
val results = holder.retrieveCredentials(
activity = activity,
credentialOffer = offer,
- clientId = "any-value",
+ clientId = "issuer-provided-client-id"
)
```
Issuance flows that previously worked with an arbitrary `client_id` will fail if the issuer requires
a trusted wallet provider. Ensure you have coordinated with each issuer and obtained the correct
`client_id` before upgrading. Test direct issuance flows to confirm credentials are issued
successfully.
### Update credential retrieval result handling [#update-credential-retrieval-result-handling]
`RetrieveCredentialResult` has been converted from a data class with nullable fields to a sealed
interface with explicit `Success` and `Failure` subtypes. Each subtype carries guaranteed
properties, removing ambiguity from result handling.
The `retrieveCredentials` function returns `List`, a list with one result
per offered credential. Update your iteration logic to use `when` matching:
```diff
val results = holder.retrieveCredentials(options)
for (result in results) {
- val credentialId = result.credentialId
- if (credentialId != null) {
- // Use result.docType and credentialId
- } else {
- // Use result.docType and result.error
- }
+ when (result) {
+ is RetrieveCredentialResult.Success -> {
+ // result.docType and result.credentialId are guaranteed
+ }
+ is RetrieveCredentialResult.Failure -> {
+ // result.docType and result.error are guaranteed
+ }
+ }
}
```
Update tests and any downstream logic that checked for `null` fields.
### `doctype` renamed to `docType` [#doctype-renamed-to-doctype]
The `doctype` field has been renamed to `docType` (camelCase). This affects:
* [`OfferedCredential`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder.issuance.dto/-offered-credential/index.html)
returned by [`discoverCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-android/latest/m-docs%20-holder%20-s-d-k/global.mattr.mobilecredential.holder/-mobile-credential-holder/discover-credential-offer.html)
Update all references from `.doctype` to `.docType`.
```diff
- val documentType = credential.doctype
+ val documentType = credential.docType
```
### Rename `AuthenticationOption` to `DeviceAuthenticationOption` [#rename-authenticationoption-to-deviceauthenticationoption]
`AuthenticationOption` has been renamed to `DeviceAuthenticationOption`. Update all imports and type
references, and rename the `authenticationOption` argument on `createProximityPresentationSession` to
`deviceAuthenticationOption`:
```diff
- import global.mattr.mobilecredential.holder.AuthenticationOption
+ import global.mattr.mobilecredential.holder.DeviceAuthenticationOption
- val authOption: AuthenticationOption = ...
+ val authOption: DeviceAuthenticationOption = ...
- holder.createProximityPresentationSession(authenticationOption = authOption)
+ holder.createProximityPresentationSession(deviceAuthenticationOption = authOption)
```
### Handle optional `OfferedCredential.claims` [#handle-optional-offeredcredentialclaims]
`OfferedCredential.claims` is now optional and only returned for offers that contain claim data.
Handle the case where `claims` is `null`:
```diff
- val claims = offeredCredential.claims
+ val claims = offeredCredential.claims // may be null when the offer contains no claim data
+ if (claims != null) {
+ // Use the claims
+ }
```
### Update `matchedCredentials` handling for online presentation [#update-matchedcredentials-handling-for-online-presentation]
`OnlinePresentationSession.matchedCredentials` has been changed from a property to a
`getMatchedCredentials()` method to support Just In Time issuance flows. Replace property access with
the method call:
```diff
- val matched = session.matchedCredentials
+ val matched = session.getMatchedCredentials()
```
### Update `terminateSession` calls [#update-terminatesession-calls]
The `sessionStatus` parameter has been removed from `ProximityPresentationSession.terminateSession`.
Sessions terminated this way send `SessionStatus.SessionTerminated` to the verifier. If you
previously passed a custom status, listen for `SessionStatus.SessionTerminated` in your presentation
session callback and handle it as needed:
```diff
- session.terminateSession(sessionStatus = customStatus)
+ session.terminateSession()
```
### Supply new `DiscoveredCredentialOffer` fields [#supply-new-discoveredcredentialoffer-fields]
The required fields `authorizationServerIssuer` and `tokenEndpointAuthMethodsSupported` have been
added to `DiscoveredCredentialOffer`. If you construct this type directly, supply the new arguments.
Most callers receive this type from `discoverCredentialOffer` and are not affected.
### Update verifier JWT request objects [#update-verifier-jwt-request-objects]
The SDK now enforces stricter JWT request validation for online presentations:
* The `typ` header must match the expected value.
* The `iat` (issued at) claim is now validated.
* Expired `exp` (expiration) values are now rejected.
Where possible, ensure all verifiers in your ecosystem generate compliant JWT authorization request
objects. Update test fixtures and any non-compliant verifier integrations.
### Validate `state` values in presentation requests [#validate-state-values-in-presentation-requests]
Oversized `state` values in VP online presentation requests are now rejected. Where possible, ensure
verifier-generated `state` values remain within supported limits. Update tests that use large state
payloads.
### Ensure `x5c` certificate chains are present [#ensure-x5c-certificate-chains-are-present]
Missing `x5c` certificate chains in validity-signed JWT request objects are now safely rejected.
Where possible, ensure verifier request objects include required certificate chains where applicable.
Update negative-path handling for rejected presentation requests.
### Validate credential payload sizes [#validate-credential-payload-sizes]
Oversized credential fields in pre-authorized issuance are now rejected. Where possible, validate
issuer payload sizes and test issuance with realistic credential data. Update error handling for
rejected oversized credentials:
```diff
val result = holder.retrieveCredentials(options)
when (result) {
is RetrieveCredentialsResult.Failure -> {
when (result.error) {
// ... existing error cases
+ is OversizedCredentialPayload -> {
+ // Handle oversized credential rejection
+ }
}
}
}
```
### Handle optional `mdocIacasUri` [#handle-optional-mdociacasuri]
The `mdocIacasUri` field is now optional. Review any app-side assumptions that this field is always
present and confirm handling of empty string values:
```diff
- val iacasUri = credential.mdocIacasUri // assumed non-null
+ val iacasUri = credential.mdocIacasUri // may be empty string
+ if (iacasUri.isNotEmpty()) {
+ // Use the URI
+ }
```
# iOS Holder SDK v5.0.0 Migration Guide
URL: /docs/holding/sdk-operations/migration-guides/ios-5.0.0-migration-guide
## Overview [#overview]
This guide provides a comprehensive overview of the changes introduced in MobileCredentialHolderSDK v5.0.0 for iOS, including breaking changes, new features, and migration steps.
## Key Features [#key-features]
* **Seamless, OS-native credential presentation (DC API support)**: The iOS Holder SDK now integrates with the Digital Credentials API, allowing credentials to be presented via an OS overlay without launching your holder app. The OS automatically surfaces only matching credentials across installed holders — reducing friction, avoiding dead ends, and significantly improving the user experience.
* **Clearer OID4VCI interoperability (v1.0 alignment)**: The iOS Holder SDK now aligns with the finalized OID4VCI v1.0 specification (upgraded from draft-12). This alignment improves interoperability across the ecosystem, enabling other systems to clearly identify your supported version and feature set, ensuring smoother integrations and consistent behavior across platforms.
* **Improved reliability in contactless flows**: Enhanced BLE performance delivers more consistent proximity credential exchanges and faster engagements.
* **Stronger cryptography and standards alignment**: Updated COSE algorithms (as per RFC 9864) strengthen cryptographic compatibility and ensure continued compliance with evolving standards.
* **General stability and performance improvements**: Multiple refinements reduce integration friction, increase consistency across mobile environments, and improve overall user experience.
For a detailed list of changes included in this release, refer to the [SDK Changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/changelog).
## Breaking Changes [#breaking-changes]
This section outlines the breaking changes introduced in v5.0.0 that require updates to your existing implementation:
| # | Element | Change | Impact |
| - | --------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| 1 | `MobileCredentialHolder.getCredential(credentialId:skipStatusCheck:)` | Parameter renamed to `fetchUpdatedStatusList:` with **inverted semantics** | All call sites using `skipStatusCheck:` must be updated. |
| 2 | `MobileCredentialHolder.deinitialize()` | No longer `async` | Remove `await` from all call sites. |
| 3 | `MobileCredentialHolder.initialize(...)` | New `dcConfiguration:` param + `@available(iOSApplicationExtension, unavailable)` | Extensions must use new `initializeAppExtension`. |
| 4 | `OfferedCredential` | New required property `credentialConfigurationId: String` | Manual decoders must handle new field. |
| 5 | `getCurrentLogFilePath()` | New `appGroup:` param + unavailable in extensions | Source compatible in apps; breaks extensions. |
| 6 | `VerifierAuthenticationResult` | New enum case `.unsigned(origin: String?)` | Exhaustive `switch` statements must add new case. |
| 7 | 20+ methods (see list below) | Now `@available(iOSApplicationExtension, unavailable)` | Extensions must migrate to DC APIs. |
| 8 | Xcode / Toolchain | SDK built with Xcode 26.0.0 | Requires Xcode 26.0.0+. Builds will fail on earlier toolchains (e.g. Xcode 16.4). CI environments must be upgraded. |
The following methods are **unavailable** in App Extensions:
* Initialization and lifecycle:
* `initialize`
* `deinitialize`
* `destroy`
* Credential claiming:
* `discoverCredentialOffer`
* `createAuthorizationSession`
* `retrieveCredentials`
* Credential management:
* `addCredential`
* `deleteCredential`
* `getCredentials`
* `getCredential`
* Key and certificate management:
* `generateDeviceKey`
* `addTrustedIssuerCertificates`
* `getTrustedIssuerCertificates`
* `deleteTrustedIssuerCertificate`
* `addTrustedVerifierCertificates`
* `getTrustedVerifierCertificates`
* `deleteTrustedVerifierCertificate`
* Presentation sessions:
* `createProximityPresentationSession`
* `getCurrentProximityPresentationSession`
* `createOnlinePresentationSession`
* `ProximityPresentationSession.sendResponse`
* `terminateSession`
* `OnlinePresentationSession.sendResponse`
* Logging:
* `getCurrentLogFilePath`
## New Additions [#new-additions]
### Types (iOS 26+) [#types-ios-26]
| Type | Purpose |
| ---------------------------------- | ------------------------------------------------------------------- |
| `DCConfiguration` | Configure Digital Credentials support (appGroup, supportedDocTypes) |
| `DCConfiguration.SupportedDocType` | Enum: `.mDL`, `.eudi`, `.euav`, `.photoid`, `.jpMnc` |
| `DCError` | Errors for DC operations |
| `DCPresentationSession` | Handle system-initiated credential requests |
### Methods [#methods]
| Method | Availability | Purpose |
| ------------------------------------------------------------------ | ------------ | ----------------------------------------------------------------- |
| `initializeAppExtension(instanceID:appGroup:loggerConfiguration:)` | iOS 26+ | Initialize SDK in Identity Document Provider extension |
| `createDcPresentationSession(from:)` | iOS 26+ | Create session from system `ISO18013MobileDocumentRequestContext` |
| `performRegistrationUpdates()` | iOS 26+ | Sync credentials with system Identity Document Store |
### Enum Cases [#enum-cases]
| Type | New Case |
| ------------------------------ | --------------------------------- |
| `VerifierAuthenticationResult` | `.unsigned(origin: String?)` |
| `MobileCredentialHolderError` | `.storageInitializedInBackground` |
### Protocol Conformances [#protocol-conformances]
| Type | Added Conformance |
| ----------------------------- | ----------------- |
| `VerifierAuthenticationError` | `Encodable` |
### Framework Imports [#framework-imports]
```swift
import IdentityDocumentServices // iOS 26+
import IdentityDocumentServicesUI // iOS 26+
```
## Backwards Compatible Changes [#backwards-compatible-changes]
| Change | Note |
| ---------------------------------------------- | -------------------------------------------- |
| `MobileCredentialDataTypes` nested typealiases | `@available` removed (inherited from parent) |
| `OnlinePresentationSession.VerifiedBy` | `@available` removed (inherited from parent) |
## Deprecations [#deprecations]
No new deprecations. Existing deprecations unchanged:
```swift
@available(*, deprecated, message: "Use .userPresence instead")
case biometricOrPasscode
@available(*, deprecated, message: "Use .biometryCurrentSet instead")
case biometricOnly
```
## Bug Fixes [#bug-fixes]
* Fixed intermittent "unable to initialize storage" errors during app launch. The issue was caused by using `UserDefaults` for `keyId` storage, which does not guarantee persistence—particularly during iOS app prewarming when protected data may be unavailable. The SDK now stores the `keyId` in the Keychain and performs explicit availability checks before initialization, throwing a new `storageInitializedInBackground` error when the keychain is inaccessible.
* Fixed a crash that could occur when `getCredentials()` or `updateMobileCredentialStatus()` were called concurrently from multiple threads. The crash was caused by unsafe concurrent access to internal state. The SDK now properly serializes access to shared resources during credential operations.
## Minimum Requirements [#minimum-requirements]
* iOS 15+ for core SDK functionality
* iOS 26+ for Digital Credentials API (DC API) features
## Dependencies [#dependencies]
**Third party dependencies**
* [CBORCoding](https://github.com/SomeRandomiOSDev/CBORCoding) (MIT license)
* [swift-certificates](https://github.com/apple/swift-certificates.git) 1.7.0 (Apache-2.0 License)
* [swift-asn1](https://github.com/apple/swift-asn1) 1.3.1 (Apache-2.0 License)
**Apple frameworks**
* [Security](https://developer.apple.com/documentation/security)
* [CryptoKit](https://developer.apple.com/documentation/cryptokit/)
* [LocalAuthentication](https://developer.apple.com/documentation/localauthentication)
* [AuthenticationServices](https://developer.apple.com/documentation/authenticationservices)
* [CoreBluetooth](https://developer.apple.com/documentation/corebluetooth)
* [Combine](https://developer.apple.com/documentation/combine)
* [OSLog](https://developer.apple.com/documentation/oslog)
* [UIKit](https://developer.apple.com/documentation/uikit)
* [AppKit (on macOS)](https://developer.apple.com/documentation/appkit)
* [IdentityDocumentServices](https://developer.apple.com/documentation/IdentityDocumentServices)
* [IdentityDocumentServicesUI](https://developer.apple.com/documentation/IdentityDocumentServicesUI)
**Toolchain dependencies**
* Swift 5.10.
* iOS support: The SDK functionality is only available for devices from iOS 15 onwards.
* Xcode: The SDK requires Xcode 26.0.0 (17A324) or later. Our CI builds use the latest stable Xcode available in GitHub Actions (setup-xcode action with the `latest-stable` label). Ensure that your development environment and CI pipelines meet or exceed this minimum to avoid build failures.
* macOS 15 or later.
## Migration Steps [#migration-steps]
### Update `getCredential` calls [#update-getcredential-calls]
The `skipStatusCheck` parameter has been renamed to `fetchUpdatedStatusList` with inverted semantics. When `fetchUpdatedStatusList` is `true` (default), the SDK will fetch the latest status list to ensure up-to-date revocation information. When `false`, it will skip fetching the status list. Update all calls accordingly:
```diff
- let credential = try await holder.getCredential(credentialId: id, skipStatusCheck: true)
+ let credential = try await holder.getCredential(credentialId: id, fetchUpdatedStatusList: false)
```
| Old Parameter | New Parameter | Mapping |
| ---------------------------------- | ---------------------------------------- | ------------------ |
| `skipStatusCheck: false` (default) | `fetchUpdatedStatusList: true` (default) | No change needed |
| `skipStatusCheck: true` | `fetchUpdatedStatusList: false` | Invert the boolean |
Refer to [Revocation Status check](/docs/holding/credential-claiming-guides/revocation-status-check) for more information.
### Update `deinitialize` calls [#update-deinitialize-calls]
The `deinitialize()` method is no longer `async`, so remove `await` from all call sites:
```diff
- await MobileCredentialHolder.shared.deinitialize()
+ MobileCredentialHolder.shared.deinitialize()
```
### Handle `OfferedCredential` changes [#handle-offeredcredential-changes]
The `OfferedCredential` struct now includes a new required property `credentialConfigurationId: String`. If you are using the default decoding provided by the SDK, no changes are needed. However, if you have implemented a custom decoder for `OfferedCredential`, you must update it to handle the new field:
```diff
struct OfferedCredential: Decodable {
+ let credentialConfigurationId: String
let docType: String
let claims: [Claim]
let name: String?
}
```
### Handle `VerifierAuthenticationResult` changes [#handle-verifierauthenticationresult-changes]
The `VerifierAuthenticationResult` enum now includes a new case `.unsigned(origin: String?)` to represent unsigned/unauthenticated requests. If you have any exhaustive `switch` statements on `VerifierAuthenticationResult`, you must add a new case to handle this scenario:
```diff
switch verifierAuthenticationResult {
case .authenticated(let verifierInfo):
// Handle authenticated verifier
case .failed(let error):
// Handle authentication failure
+ case .unsigned(let origin):
+ // Handle unsigned/unauthenticated request (new in v5.0.0)
+ // origin contains the requesting website origin if available
}
```
### Enable Presenting Credentials via the Digital Credentials API (iOS 26+, optional) [#enable-presenting-credentials-via-the-digital-credentials-api-ios-26-optional]
The iOS Holder SDK v5.0.0 introduces support for the Digital Credentials API, allowing credentials to be presented via an OS-native overlay without launching your app. To enable this feature, you need to initialize the SDK with a `DCConfiguration` that specifies your supported document types and app group. This is optional but recommended for a seamless user experience.
```swift
// In main app initialization
if #available(iOS 26.0, *) {
let dcConfig = DCConfiguration(
appGroup: "group.com.yourcompany.app",
supportedDocTypes: [.mDL, .eudi]
)
try MobileCredentialHolder.shared.initialize(
instanceID: instanceID,
dcConfiguration: dcConfig
)
} else {
try MobileCredentialHolder.shared.initialize(instanceID: instanceID)
}
```
### App Extension Migration (iOS 26+) [#app-extension-migration-ios-26]
The methods marked as `@available(iOSApplicationExtension, unavailable)` are no longer accessible from app extensions. If you have an Identity Document Provider extension, you must migrate to using the new Digital Credentials APIs for handling credential requests. This involves initializing the SDK for extension use and creating presentation sessions from the system-provided context.
```swift
// In your IdentityDocumentProviderExtension
@available(iOS 26.0, *)
func handleRequest(_ context: ISO18013MobileDocumentRequestContext) async throws {
// Initialize for extension context
try MobileCredentialHolder.shared.initializeAppExtension(
appGroup: "group.com.yourcompany.app"
)
// Create presentation session
let session = try MobileCredentialHolder.shared.createDcPresentationSession(from: context)
// Handle request...
try await session.sendResponse(credentialIDs: selectedCredentialIDs)
}
```
### Update Toolchain [#update-toolchain]
The SDK now requires Xcode 26.0.0 (17A324) or later. Ensure that your development environment and CI pipelines are updated to use Xcode 26.0.0+ to avoid build failures.
# iOS Holder SDK v6.0.0 Migration Guide
URL: /docs/holding/sdk-operations/migration-guides/ios-6.0.0-migration-guide
Description: A comprehensive guide to migrating to iOS Holder SDK v6.0.0, covering breaking changes, new features, and step-by-step migration instructions.
## Overview [#overview]
This guide provides a comprehensive overview of the changes introduced in the iOS Holder SDK
v6.0.0, including breaking changes, new features, and migration steps.
This release focuses on strengthening trust at issuance, improving predictability in credential
handling, and increasing consistency across platforms. It introduces optional SDK Tethering to
connect the SDK to your MATTR VII tenant and optional Wallet Attestation support, enabling issuers
to verify wallet integrity before issuing credentials. Together, these changes make holder-side
integrations more reliable and easier to reason about in production environments.
SDK Tethering (and Wallet Attestation, which builds on it) is **optional** in this release. You opt
in by passing a `platformConfiguration` when initializing the SDK. If you omit it, the SDK skips
registration, tethering, and wallet attestation, and existing integrations continue to work. We
expect to make SDK Tethering required in an upcoming release, so we recommend adopting it now to
prepare.
## Key Features [#key-features]
* **SDK Tethering (optional)**: The iOS Holder SDK can now be tethered to a MATTR VII tenant, tying
each SDK/app instance to your tenant. This allows you to view details about registered and active
app instances directly from your tenant for operational insights. SDK Tethering also establishes a
remote management channel that we expect to extend in the future, such as remote syncing of
trusted issuer lists and eventing. Tethering is enabled by providing a `platformConfiguration` at
initialization.
* **Wallet Attestation support (optional)**: Building on the SDK Tethering channel, the iOS Holder
SDK now supports Wallet Attestation, allowing your holder application to prove to issuers that it
is a trusted wallet before credentials are issued. When an issuer requires it, the SDK uses the
tethering connection to obtain wallet attestation tokens from your MATTR VII tenant. Wallet
Attestation requires SDK Tethering to be configured.
* **Stronger application identity during issuance**: Pre-authorized credential issuance flows now
pass the application's `client_id`, ensuring the holder is accurately represented when interacting
with issuers. This improves compatibility with issuers applying stricter controls.
* **More predictable credential retrieval results**: Credential retrieval responses are now more
structured and deterministic, explicitly identifying success or failure with guaranteed fields per
result type.
* **Cross-platform alignment**: Naming and response structures have been aligned with the Android
Holder SDK, minimizing divergence for teams maintaining cross-platform applications.
* **Improved biometric and storage lifecycle handling**: Edge cases in biometric authentication,
storage lifecycle, and app lifecycle events (such as prewarming or reinstall scenarios) have been
addressed.
* **General stability and performance improvements**: Multiple refinements reduce integration
friction, increase consistency, and improve overall reliability.
## Breaking Changes [#breaking-changes]
This section outlines the breaking changes introduced in v6.0.0 that require updates to your
existing implementation:
| # | Change | Impact |
| -- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 1 | `initialize` and `initializeAppExtension` are now asynchronous | Add `await` to all call sites and call these methods from an asynchronous context. |
| 2 | Pre-authorized code flow now uses the application's `client_id` instead of a default identifier | Ensure your application has a valid configured `client_id` and issuers recognize it. |
| 3 | Credential retrieval result shape updated to explicitly identify success or failure with guaranteed fields | Update result parsing logic to use success/failure branching. |
| 4 | `MobileCredentialAuthenticationOption` renamed to `DeviceAuthenticationOption` (and the `mobileCredentialAuthenticationOption:` parameter renamed to `deviceAuthenticationOption:`) | Update all imports, type references, and session-creation parameter names. |
| 5 | `OnlinePresentationSession.matchedCredentials` is now the `getMatchedCredentials()` method and returns a non-optional array | Replace property access with the method call and remove optional handling. |
| 6 | `VerificationResult` renamed to `MobileCredentialVerificationResult`, with `.reason` renamed to `.failureType` | Update all type references, rename `.reason` to `.failureType`, and remove use of `VerificationFailedReason`. |
| 7 | Wallet Attestation introduces new error cases on `MobileCredentialHolderError` and `RetrieveCredentialErrorType` | Update error handling, logging, analytics, and support diagnostics to account for the new wallet attestation error cases. |
| 8 | `challenge` in `requestMobileCredentials` is now optional to align with Android | Remove assumptions that `challenge` must always be supplied. |
| 9 | `MobileCredential.claims` and `MobileCredentialMetadata.claims` are now non-optional, and credentials with empty claims are rejected | Remove optional chaining on `claims` and handle the decoding error where credentials are retrieved or added. |
| 10 | `MobileCredentialResponse.credentials` and `MobileCredentialResponse.credentialErrors` are now non-optional arrays | Remove optional handling on these properties. |
| 11 | `docType` is now serialized under the JSON key `docType` (previously `doctype`) | Update any persisted or parsed serialized forms to use `docType`. |
| 12 | A `.none` case was added to `UserAuthenticationType`, and device-key auth types are now non-optional | Pass `DeviceKeyAuthenticationPolicy(type: .none)` instead of `nil` to disable authentication for a device key. |
| 13 | The `claims` property on `OfferedCredential` is now optional (`[Claim]?`), aligning with the OID4VCI 1.0 specification | Handle the case where `OfferedCredential.claims` is `nil`, indicating the offer contains no claim data. |
| 14 | New required `tokenEndpointAuthMethodsSupported`, `authorizationServerIssuer`, and `nonceEndpoint` properties added to `DiscoveredCredentialOffer` | Supply the new arguments if you construct this type directly, and update any stored representations. |
| 15 | `MobileCredentialVerificationFailureType` and `TrustedCertificateVerificationFailureType` now serialize as a `{type, message}` object instead of a plain raw-value string | Update any storage or transport layer that persists or forwards these serialized values. |
| 16 | Remote mobile (app-to-app) verification now maps the backend `IssuerNotTrusted` failure to `MobileCredentialVerificationFailureType.TrustedIssuerCertificateNotFound` | Remove any `IssuerNotTrusted` handling and handle `TrustedIssuerCertificateNotFound` instead. |
| 17 | The internal storage location for app extension logs has changed | No code change required (the same `getCurrentLogFilePath(appGroup:)` method is used), but app extension logs written before the upgrade become inaccessible. |
SDK initialization does **not** require platform/tenant configuration in this release. The
`platformConfiguration` parameter is optional. Provide it only if you want to enable SDK Tethering
and Wallet Attestation. See [Update SDK initialization](#update-sdk-initialization).
## Migration Steps [#migration-steps]
### Make `initialize` calls asynchronous [#make-initialize-calls-asynchronous]
The `initialize` and `initializeAppExtension` methods are now asynchronous. Add `await` to all call
sites and call them from an asynchronous context:
```diff
- try MobileCredentialHolder.shared.initialize(instanceID: instanceID)
+ try await MobileCredentialHolder.shared.initialize(instanceID: instanceID)
```
### (Optional) Create a holder application on your MATTR VII tenant [#optional-create-a-holder-application-on-your-mattr-vii-tenant]
SDK Tethering is **optional** in this release. If you do not need it, you can skip this step and the
next one. The SDK continues to function without a `platformConfiguration`.
To enable SDK Tethering, the iOS Holder SDK connects to a MATTR VII tenant. Tethering gives you
access to a centralized view of registered and active app instances in MATTR VII, the optional
Wallet Attestation feature, and other centralized management capabilities we plan to deliver over
this channel.
We expect SDK Tethering to become required in an upcoming release. We recommend adopting it now to
prepare, even if you do not yet use Wallet Attestation.
To register your iOS application, make a request to create a holder application:
```http title="Request"
POST /v1/holder/applications
```
```json title="Request body"
{
"name": "My iOS Holder Application",
"clientId": "your-wallet-client-id",
"type": "ios",
"bundleId": "com.yourcompany.holderapp",
"teamId": "YOUR_APPLE_TEAM_ID"
}
```
* `name`: A unique name to identify your holder application.
* `clientId`: The OAuth 2.0 `client_id` that identifies your wallet application. This value is
included in attestation JWTs and must match the `client_id` configured on the issuer's
Authorization Server.
* `type`: Must be `ios` for an iOS application.
* `bundleId`: The Bundle ID of your iOS app (must match your Xcode project configuration).
* `teamId`: Your Apple Developer Team ID.
Once created, your holder application will be able to use the SDK and interact with the MATTR VII
platform (for example, to obtain attestation tokens).
The response will include a unique `id` for your application, which must be used when initializing
the SDK so that it can correctly identify and authenticate your application.
The `clientId` you configure here is the same value you must:
1. Pass as the `clientId` parameter when calling [`retrieveCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/retrievecredentials\(credentialoffer:clientid:transactioncode:devicekeyauthenticationpolicy:\)).
2. Register with each issuer you intend to interact with, so the issuer can identify and trust
requests coming from your wallet application.
### (Optional) Enable tethering at SDK initialization [#optional-enable-tethering-at-sdk-initialization]
To enable SDK Tethering, pass a `platformConfiguration` when initializing the SDK. This parameter is
optional. Omit it to initialize the SDK without tethering:
```diff
- try await MobileCredentialHolder.shared.initialize(instanceID: instanceID)
+ let platformConfig = PlatformConfiguration(
+ tenantHost: URL(string: "https://your-tenant.vii.mattr.global")!,
+ applicationId: "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
+ )
+ try await MobileCredentialHolder.shared.initialize(
+ instanceID: instanceID,
+ platformConfiguration: platformConfig
+ )
```
* `tenantHost`: The URL of your MATTR VII tenant. This must be the tenant where your holder
application is configured.
* `applicationId`: The `id` of your configured MATTR VII holder application.
When `platformConfiguration` is omitted, the SDK skips registration and tethering, and wallet
attestation is not available.
### Handle new error cases [#handle-new-error-cases]
This release adds new cases to `MobileCredentialHolderError`, which is a **breaking change** that
requires updates to exhaustive `switch` statements regardless of whether you enable SDK Tethering:
* `invalidLicense`: Thrown when the SDK license is invalid or expired.
* `failedToRegister`: Thrown when app registration with the MATTR VII tenant fails.
* `invalidWalletAttestation`: Thrown when the authorization server rejects the wallet attestation
during credential retrieval.
* `calledFromAppExtension`: Thrown when an SDK API that is unavailable in app extensions is called
from an app extension at runtime.
If you enable SDK Tethering and Wallet Attestation, the following per-credential failure types are
also added to `RetrieveCredentialErrorType`:
* `walletAttestationFailed`: Returned when wallet attestation generation fails.
* `invalidWalletAttestation`: Returned when the attestation is rejected by the credential endpoint.
The [`retrieveCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/retrievecredentials\(credentialoffer:clientid:transactioncode:devicekeyauthenticationpolicy:\))
method may now throw `invalidWalletAttestation` and return the new per-credential failure types.
Wallet attestation errors only occur when the SDK is tethered and an issuer requires attestation.
Update your error handling, logging, analytics, and support diagnostics to account for these new
error cases. Ensure any SDK API calls from app extensions are wrapped in appropriate availability
checks to prevent runtime errors.
### Update `client_id` configuration [#update-client_id-configuration]
Previously, the `client_id` passed to
[`retrieveCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/mobilecredentialholder/retrievecredentials\(credentialoffer:clientid:transactioncode:devicekeyauthenticationpolicy:\))
was not shared with the issuer during the pre-authorized code flow, so any value would work. This is
no longer the case. The SDK now presents the `client_id` to the issuer as part of wallet
attestation, and the issuer validates it against its list of trusted wallet providers.
To prepare for this change:
1. **Coordinate with the issuer** to register your wallet application as a trusted wallet provider.
The issuer will provide you with a `client_id` that identifies your application.
2. **Pass the issuer-provided `client_id`** when calling `retrieveCredentials`.
```diff
let results = try await holder.retrieveCredentials(
credentialOffer: offer,
- clientId: "any-value",
+ clientId: "issuer-provided-client-id"
)
```
Issuance flows that previously worked with an arbitrary `client_id` will fail if the issuer requires
a trusted wallet provider. Ensure you have coordinated with each issuer and obtained the correct
`client_id` before upgrading. Test direct issuance flows to confirm credentials are issued
successfully.
### Update credential retrieval result handling [#update-credential-retrieval-result-handling]
`RetrieveCredentialResult` has been converted from a struct with optional fields to an enum with
explicit `.success` and `.failure` cases. Each case carries guaranteed associated values, removing
ambiguity from result handling.
The `retrieveCredentials` function returns `[RetrieveCredentialResult]`, an array with one result
per offered credential. Update your iteration logic to use pattern matching:
```diff
let results = try await holder.retrieveCredentials(options)
for result in results {
- if let credentialId = result.credentialId {
- // Use result.docType and credentialId
- } else if let error = result.error {
- // Use result.docType and error
- }
+ switch result {
+ case .success(let docType, let credentialId):
+ // docType and credentialId are guaranteed
+ case .failure(let docType, let error):
+ // docType and error are guaranteed
+ }
}
```
Update tests and any downstream logic that checked for `nil` fields.
### Rename `MobileCredentialAuthenticationOption` to `DeviceAuthenticationOption` [#rename-mobilecredentialauthenticationoption-to-deviceauthenticationoption]
Update all imports, type references, and configuration objects:
```diff
- let authOption: MobileCredentialAuthenticationOption = .biometryCurrentSet
+ let authOption: DeviceAuthenticationOption = .biometryCurrentSet
```
### Update `matchedCredentials` handling for online presentation [#update-matchedcredentials-handling-for-online-presentation]
The optional `matchedCredentials` property on `OnlinePresentationSession` has been replaced by a
`getMatchedCredentials()` method that returns a non-optional array. Replace property access (and any
optional handling) with the method call:
```diff
- if let matched = session.matchedCredentials {
- // Handle matched credentials
- } else {
- // Handle nil case
- }
+ // getMatchedCredentials() always returns an array (may be empty)
+ let matched = session.getMatchedCredentials()
+ if matched.isEmpty {
+ // Handle no matched credentials
+ } else {
+ // Handle matched credentials
+ }
```
### Update `VerificationResult` to `MobileCredentialVerificationResult` [#update-verificationresult-to-mobilecredentialverificationresult]
The `VerificationResult` type has been renamed to `MobileCredentialVerificationResult` and aligned
structurally with Android. Its `reason` property has been renamed to `failureType`, typed directly
as `MobileCredentialVerificationFailureType?` rather than the now-removed `VerificationFailedReason`
wrapper:
```diff
- let result: VerificationResult = ...
+ let result: MobileCredentialVerificationResult = ...
- let failure = result.reason
+ let failure = result.failureType
```
Replace all references to `VerificationResult` with `MobileCredentialVerificationResult`, rename
`.reason` to `.failureType`, and remove any usage of `VerificationFailedReason`. The same `.reason` →
`.failureType` rename applies to `TrustedCertificateVerificationResult`. You will also need to
validate cross-platform result handling and update any downstream mapping logic.
### Handle optional `challenge` in `requestMobileCredentials` [#handle-optional-challenge-in-requestmobilecredentials]
The `challenge` parameter is now optional in `requestMobileCredentials`. Review call sites and
validation logic, and remove any app-side assumptions that `challenge` must always be supplied:
```diff
- // challenge was previously required
- let request = try holder.requestMobileCredentials(challenge: challenge, ...)
+ // challenge is now optional
+ let request = try holder.requestMobileCredentials(challenge: challenge, ...) // still works
+ let request = try holder.requestMobileCredentials(...) // also valid without challenge
```
### Remove optional handling on `claims` [#remove-optional-handling-on-claims]
`MobileCredential.claims` and `MobileCredentialMetadata.claims` are now non-optional
(`[NameSpace: [ElementID: ElementValue]]`), defaulting to `[:]` when namespaces are absent. Remove
optional chaining on these accesses:
```diff
- let value = credential.claims?["org.iso.18013.5.1"]?["family_name"]
+ let value = credential.claims["org.iso.18013.5.1"]?["family_name"]
```
Retrieving or decoding a credential whose `IssuerSigned` data contains no namespaces (or a namespace
with no claims) now fails with a decoding error instead of producing a credential with empty claims.
Handle this decoding error where credentials are retrieved or added.
### Remove optional handling on `MobileCredentialResponse` collections [#remove-optional-handling-on-mobilecredentialresponse-collections]
`MobileCredentialResponse.credentials` and `MobileCredentialResponse.credentialErrors` are now
non-optional arrays that default to empty, instead of optional arrays that could be `nil`. Remove
optional handling on these properties:
```diff
- for credential in response.credentials ?? [] { ... }
+ for credential in response.credentials { ... }
```
### Update `docType` serialization [#update-doctype-serialization]
The document type in `RetrieveCredentialResult` and `OfferedCredential` is now encoded under the
JSON key `docType` instead of `doctype`. Update any code that persists or parses these serialized
forms to use `docType`.
### Update `UserAuthenticationType` handling [#update-userauthenticationtype-handling]
A `.none` case was added to `UserAuthenticationType` to explicitly disable user authentication.
Consequently, `DeviceKeyAuthenticationInfo.type` and `DeviceKeyAuthenticationPolicy.type` are now
non-optional `UserAuthenticationType`, and `DeviceKeyAuthenticationPolicy(type:)` no longer accepts
`nil`. To disable authentication for a specific device key, pass `.none` instead of `nil`:
```diff
- let policy = DeviceKeyAuthenticationPolicy(type: nil)
+ let policy = DeviceKeyAuthenticationPolicy(type: .none)
```
### Handle optional `OfferedCredential.claims` [#handle-optional-offeredcredentialclaims]
The `claims` property on [`OfferedCredential`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/offeredcredential)
is now optional (`[Claim]?`), aligning with the [OID4VCI 1.0 specification](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-12.2.4).
It is only populated for offers that contain claim data. Handle the case where `claims` is `nil`:
```diff
- let claims = offeredCredential.claims
+ let claims = offeredCredential.claims // may be nil when the offer contains no claim data
+ if let claims {
+ // Use the claims
+ }
```
### Supply new `DiscoveredCredentialOffer` properties [#supply-new-discoveredcredentialoffer-properties]
The required `tokenEndpointAuthMethodsSupported`, `authorizationServerIssuer`, and `nonceEndpoint`
properties have been added to [`DiscoveredCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-ios/latest/documentation/mobilecredentialholdersdk/discoveredcredentialoffer):
* `tokenEndpointAuthMethodsSupported`: An array of authentication methods supported by the token
endpoint.
* `authorizationServerIssuer`: The issuer identifier of the authorization server.
* `nonceEndpoint`: The authorization server nonce endpoint used for OID4VCI proof-of-possession.
The public initializer now requires these arguments and the encoded representation includes the
corresponding fields. Most callers receive this type from `discoverCredentialOffer` and are not
affected. If you construct or decode `DiscoveredCredentialOffer` directly, update your call sites and
any stored representations.
### Update failure-type serialization handling [#update-failure-type-serialization-handling]
`MobileCredentialVerificationFailureType` and `TrustedCertificateVerificationFailureType` now encode
and decode as a `{type, message}` object instead of a plain raw-value string:
```diff
- "TrustedIssuerCertificateNotFound"
+ {"type": "TrustedIssuerCertificateNotFound", "message": "Trusted issuer certificate not found"}
```
If you persist or forward the serialized value of either failure type, update your storage or
transport layer to produce and consume the new format.
### Update issuer-trust failure handling in remote mobile verification [#update-issuer-trust-failure-handling-in-remote-mobile-verification]
During remote mobile (app-to-app) verification, a backend `IssuerNotTrusted` failure reason is now
decoded as `MobileCredentialVerificationFailureType.TrustedIssuerCertificateNotFound` instead of
being surfaced as `IssuerNotTrusted` via the now-removed `VerificationFailedReason` wrapper. Remove
any `IssuerNotTrusted` handling and update it to handle `TrustedIssuerCertificateNotFound` as the
failure type for issuer trust failures:
```diff
- case .issuerNotTrusted:
+ case .trustedIssuerCertificateNotFound:
// Handle issuer trust failure
```
### Note the app extension log location change [#note-the-app-extension-log-location-change]
The internal storage location for app extension logs has changed. Logs are still retrieved with the
same `getCurrentLogFilePath(appGroup:)` method, so **no code change is required**. However, any app
extension logs written before upgrading (up to the 48-hour retention window) will no longer be
accessible after the upgrade. The location for main SDK logs remains unchanged.
# React Native Holder SDK v10.0.0 Migration Guide
URL: /docs/holding/sdk-operations/migration-guides/react-native-10.0.0-migration-guide
Description: A comprehensive guide to migrating to React Native Holder SDK v10.0.0, covering breaking changes, new features, and step-by-step migration instructions.
This is a **draft** migration guide, shared ahead of general availability to help you gauge migration effort and plan ahead of the release. We will update this page to a final copy when the SDK is generally available.
## Overview [#overview]
This guide provides a comprehensive overview of the changes introduced in React Native Holder SDK v10.0.0, including breaking changes, new features, and migration steps.
This release focuses on strengthening trust at issuance, improving predictability in credential handling, and increasing consistency across platforms. It introduces optional SDK Tethering to connect the SDK to your MATTR VII tenant, adds optional Wallet Attestation support to enable issuers to verify wallet integrity, and adds support in the JavaScript/TypeScript layer to bridge the new platform configuration.
SDK Tethering (and Wallet Attestation, which builds on it) is **optional** in this release. You opt in by passing a `platformConfiguration` when initializing the SDK. If you omit it, the SDK skips registration, tethering, and wallet attestation, and existing integrations continue to work. We expect to make SDK Tethering required in an upcoming release, so we recommend adopting it now to prepare.
## Key Features [#key-features]
* **SDK Tethering (optional)**: The React Native Holder SDK can now be tethered to a MATTR VII tenant, tying each SDK/app instance to your tenant. This allows you to view details about registered and active app instances directly from your tenant for operational insights. SDK Tethering also establishes a remote management channel that we expect to expand soon with additional features, such as remote syncing of trusted issuer lists and eventing. Tethering is enabled by providing a `platformConfiguration` at initialization.
* **Wallet Attestation support (optional)**: Building on the SDK Tethering channel, the React Native Holder SDK now supports Wallet Attestation, allowing your holder application to prove to issuers that it is a trusted wallet before credentials are issued. When an issuer requires it, the SDK uses the tethering connection to obtain wallet attestation tokens from your MATTR VII tenant. Wallet Attestation requires SDK Tethering to be configured.
* **Stronger application identity during issuance**: Pre-authorized credential issuance flows now pass the application's `client_id`, ensuring the holder is accurately represented when interacting with issuers. This improves compatibility with issuers applying stricter controls.
* **More predictable credential retrieval results**: Credential retrieval responses are now more structured and deterministic, explicitly identifying success or failure with guaranteed fields per result type.
* **General stability and performance improvements**: Multiple refinements reduce integration friction, increase consistency, and improve overall reliability.
## Breaking Changes [#breaking-changes]
This section outlines the breaking changes introduced in v10.0.0 that require updates to your existing implementation:
| # | Change | Impact |
| - | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| 1 | Pre-authorized code flow now uses the application's `client_id` instead of a default identifier | Ensure your application has a valid configured `client_id` and issuers recognize it. |
| 2 | Credential retrieval result shape updated to explicitly identify success or failure with guaranteed fields | Update result parsing and handling logic. |
| 3 | `doctype` renamed to `docType` | Rename all usages of `doctype` to `docType`. |
| 4 | `MobileCredentialAuthenticationOption` renamed to `DeviceAuthenticationOption` | Update imports, type references, and configuration objects. |
SDK initialization does **not** require platform/tenant configuration in this release. The
`platformConfiguration` option is optional — provide it only if you want to enable SDK Tethering
and Wallet Attestation. See [Enable tethering at SDK initialization](#optional-enable-tethering-at-sdk-initialization).
## Migration Steps [#migration-steps]
### (Optional) Create holder applications on your MATTR VII tenant [#optional-create-holder-applications-on-your-mattr-vii-tenant]
SDK Tethering is **optional** in this release. If you do not need it, you can skip this step and the next one — the SDK continues to function without a `platformConfiguration`.
To enable SDK Tethering, the React Native Holder SDK connects to a MATTR VII tenant via the underlying native SDKs. Tethering gives you access to a centralized view of registered and active app instances in MATTR VII, the optional Wallet Attestation feature, and other centralized management capabilities we plan to deliver over this channel.
We expect SDK Tethering to become required in an upcoming release. We recommend adopting it now to prepare, even if you do not yet use Wallet Attestation.
Since React Native targets both iOS and Android, you need to create **two** holder applications, one for each platform.
#### Create the iOS holder application [#create-the-ios-holder-application]
Make a request to create a holder application:
```http title="Request"
POST /v1/holder/applications
```
```json title="Request body"
{
"name": "My RN Holder Application (iOS)",
"clientId": "your-wallet-client-id",
"type": "ios",
"bundleId": "com.yourcompany.holderapp",
"teamId": "YOUR_APPLE_TEAM_ID"
}
```
* `name`: A unique name to identify this holder application.
* `clientId`: The OAuth 2.0 `client_id` that identifies your wallet application. This value is included in attestation JWTs and must match the `client_id` configured on the issuer's Authorization Server.
* `type`: Must be `ios` for the iOS target.
* `bundleId`: The Bundle ID of your iOS app (must match your Xcode project configuration).
* `teamId`: Your Apple Developer Team ID.
The response will include a unique `id` for your application, which must be used when initializing the SDK so that it can correctly identify and authenticate your application.
#### Create the Android holder application [#create-the-android-holder-application]
```http title="Request"
POST /v1/holder/applications
```
```json title="Request body"
{
"name": "My RN Holder Application (Android)",
"clientId": "your-wallet-client-id",
"type": "android",
"packageName": "com.yourcompany.holderapp",
"packageSigningCertificateThumbprints": [
"1232584b6f6a892d356899fb9576c5f226a179e6199f2b7a1d837b5c234c5a8e"
]
}
```
* `name`: A unique name to identify this holder application.
* `clientId`: The OAuth 2.0 `client_id` that identifies your wallet application. This value is included in attestation JWTs and must match the `client_id` configured on the issuer's Authorization Server.
* `type`: Must be `android` for the Android target.
* `packageName`: The package name of your Android application.
* `packageSigningCertificateThumbprints`: SHA-256 hex-encoded fingerprints of the signing key certificates used to sign your APK or app bundle. This ensures the tenant only accepts requests from known and trusted applications. Refer to [Android app signing](/docs/holding/android-app-signing) for more information.
The response will include a unique `id` for your application, which must be used when initializing the SDK so that it can correctly identify and authenticate your application.
Once both application configurations are created, your application will be able to use the SDK and interact with the MATTR VII platform (for example, to obtain attestation tokens).
The `clientId` you configure here is the same value you must:
1. Pass as the `clientId` in the options when calling [`retrieveCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/retrieveCredentials.html).
2. Register with each issuer you intend to interact with, so the issuer can identify and trust requests coming from your wallet application.
### (Optional) Enable tethering at SDK initialization [#optional-enable-tethering-at-sdk-initialization]
To enable SDK Tethering, pass a `platformConfiguration` through the JS/TS layer into the native SDKs when initializing. This option is optional — omit it to initialize the SDK without tethering.
Since React Native bridges both iOS and Android, and each platform has its own holder application registered on your MATTR VII tenant (see [the previous step](#optional-create-holder-applications-on-your-mattr-vii-tenant)), your initialization code must pass the correct platform-specific `applicationId` at runtime. Use `Platform.OS` to select the appropriate value:
```diff
import { initialize } from "@mattrglobal/mobile-credential-holder-react-native";
+ import { Platform } from "react-native";
- await initialize({ instanceId: "your-instance-id" });
+ const applicationId =
+ Platform.OS === "android"
+ ? "your-android-holder-application-id"
+ : "your-ios-holder-application-id";
+
+ await initialize({
+ instanceId: "your-instance-id",
+ platformConfiguration: {
+ tenantHost: "https://your-tenant.vii.mattr.global",
+ applicationId,
+ },
+ });
```
Replace the placeholder values with the `id` returned when you created each holder application. In practice, you would typically store these values in a configuration file or environment variables. When `platformConfiguration` is omitted, the SDK skips registration, tethering, and wallet attestation.
Confirm that:
* The `applicationId` used on iOS corresponds to the holder application created with your app's `bundleId` and `teamId`.
* The `applicationId` used on Android corresponds to the holder application created with your app's `packageName` and signing certificate thumbprints.
* Environment-specific tenant URLs are correctly configured.
### Handle new Wallet Attestation error modes [#handle-new-wallet-attestation-error-modes]
If you enable SDK Tethering and Wallet Attestation, new error scenarios can occur during credential issuance. These errors only arise when the SDK is tethered and an issuer requires attestation, specifically when:
* The SDK requests an attestation token from your MATTR VII tenant.
* The SDK presents wallet attestation credentials to an issuer's Authorization Server at its `/token` endpoint.
* The SDK requests credentials from an issuer's `/credential` endpoint with DPoP-bound access tokens.
The following methods are affected:
* [`retrieveCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/retrieveCredentials.html) — may return new error types in both the thrown error and per-credential results.
New error types will be added to `MobileCredentialHolderError` and `RetrieveCredentialError` (a **breaking change** requiring updates to error type handling).
Update your error handling, logging, analytics, and support diagnostics to account for new wallet attestation error cases. An exhaustive list of new errors will be provided with the final migration guide.
### Update `client_id` configuration [#update-client_id-configuration]
Previously, the `client_id` passed to
[`retrieveCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/retrieveCredentials.html)
was not shared with the issuer during the pre-authorized code flow, so any value would work. This is no longer the case — the SDK now presents the `client_id` to the issuer as part of wallet attestation, and the issuer validates it against its list of trusted wallet providers.
To prepare for this change:
1. **Coordinate with the issuer** to register your wallet application as a trusted wallet provider. The issuer will provide you with a `client_id` that identifies your application.
2. **Pass the issuer-provided `client_id`** in the options when calling `retrieveCredentials`.
```diff
const result = await retrieveCredentials({
credentialOffer: offer,
- clientId: "any-value",
+ clientId: "issuer-provided-client-id"
});
```
Issuance flows that previously worked with an arbitrary `client_id` will fail if the issuer requires a trusted wallet provider. Ensure you have coordinated with each issuer and obtained the correct `client_id` before upgrading. Test direct issuance flows to confirm credentials are issued successfully.
### Update credential retrieval result handling [#update-credential-retrieval-result-handling]
`RetrieveCredentialsResponse` has changed from an array of flat objects with optional fields to an array of `RetrieveCredentialItem` — a discriminated union with `isSuccess` as the discriminator. Each item is either a `RetrieveCredentialSuccess` (with guaranteed `docType` and `credentialId`) or a `RetrieveCredentialFailure` (with guaranteed `docType` and `error`).
Update your result iteration logic to narrow on `isSuccess`:
```diff
const result = await retrieveCredentials(options);
if (result.isOk()) {
for (const item of result.value) {
- if (item.credentialId) {
- // Use item.doctype and item.credentialId
+ if (item.isSuccess) {
+ // item.docType and item.credentialId are guaranteed
} else {
- // Use item.doctype and item.error
+ // item.docType and item.error are guaranteed
}
}
}
```
Update TypeScript type guards, tests, and any downstream logic that checked for optional fields.
### `doctype` renamed to `docType` [#doctype-renamed-to-doctype]
The `doctype` field has been renamed to `docType` (camelCase). This affects:
* [`RetrieveCredentialsResponse`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/types/RetrieveCredentialsResponse.html)
returned by [`retrieveCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/retrieveCredentials.html)
* [`OfferedCredential`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/types/OfferedCredential.html)
returned by [`discoverCredentialOffer`](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/functions/discoverCredentialOffer.html)
Update all references from `.doctype` to `.docType`.
```diff
- const documentType = credential.doctype;
+ const documentType = credential.docType;
```
### Rename `MobileCredentialAuthenticationOption` to `DeviceAuthenticationOption` [#rename-mobilecredentialauthenticationoption-to-deviceauthenticationoption]
Update all TypeScript imports, type references, and configuration objects:
```diff
- import { MobileCredentialAuthenticationOption } from "@mattrglobal/mobile-credential-holder-react-native";
+ import { DeviceAuthenticationOption } from "@mattrglobal/mobile-credential-holder-react-native";
- const authOption: MobileCredentialAuthenticationOption = ...;
+ const authOption: DeviceAuthenticationOption = ...;
```
# React Native Holder SDK v9.0.0 Migration Guide
URL: /docs/holding/sdk-operations/migration-guides/react-native-9.0.0-migration-guide
Description: A comprehensive guide to migrating to React Native Holder SDK v9.0.0, covering breaking changes, new features, and step-by-step migration instructions.
## Overview [#overview]
This guide provides a comprehensive overview of the changes introduced in React Native Holder SDK v9.0.0, including breaking changes, new features, and migration steps.
## Key Features [#key-features]
* **Seamless, OS-native credential presentation (DC API support)**: The React Native Holder SDK now integrates with the Digital Credentials API on supported platforms (Android devices running Google Play services version 24.0+ and devices running iOS 26 and later), allowing credentials to be presented via an OS overlay without launching your holder app. The OS automatically surfaces only matching credentials across installed holders, reducing friction, avoiding dead ends, and significantly improving the user experience.
* **Device key authentication support**: The React Native Holder SDK now supports defining a specific authentication method for claiming and accessing specific credentials. This allows you to control how users authenticate. For example, requiring Face ID, fingerprint, or a device passcode, depending on the sensitivity of each credential.
* **Verifier Authentication**: The Holder SDK now supports Verifier Authentication as defined in ISO/IEC 18013-5:2021. This allows a Holder app to confirm the identity of a verifier when it receives a credential request. This helps improve privacy and trust by allowing you to inform users (or prevent sharing altogether) when a credential is requested by a verifier outside your trusted network. Support for this feature requires implementation in both the Holder and Verifier apps.
* **NFC Support (Android Only)**: The React Native Holder SDK now supports using NFC to start a verification or presentation session on Android devices. This allows users to begin a proximity session by simply tapping two NFC-enabled devices together instead of scanning a QR code.
* **Status Lists Draft 14 Support**: The SDK now supports the Token Status List Draft 14 specification while maintaining existing support for Draft 3.
* **Clearer OID4VCI interoperability (v1.0 alignment)**: The React Native Holder SDK now aligns with the finalized OID4VCI v1.0 specification (upgraded from draft-12). This alignment improves interoperability across the ecosystem, enabling other systems to clearly identify your supported version and feature set, ensuring smoother integrations and consistent behavior across platforms.
* **Stronger cryptography and standards alignment**: Updated COSE algorithms (as per RFC 9864) strengthen cryptographic compatibility and ensure continued compliance with evolving standards.
* **General stability and performance improvements**: Multiple refinements reduce integration friction, increase consistency across mobile environments, and improve overall user experience.
For a detailed list of changes included in this release, refer to the [SDK Changelog](https://api-reference-sdk.mattr.global/mobile-credential-holder-react-native/latest/index.html#md:change-log).
## Breaking Changes [#breaking-changes]
| # | Element | Change | Impact |
| - | -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| 1 | `getCredential(credentialId, { skipStatusCheck = ... })` | Parameter renamed to `fetchUpdatedStatusList = ...` with **inverted semantics** | All call sites using `skipStatusCheck = ...` must be updated. |
| 2 | `initialize(...)` | Can return three new error types: `StorageInitializedInBackground`, `SdkInitialized`, `InvalidInstanceID` | Exhaustive error handling must be updated. |
| 3 | `ProximityPresentationSessionTerminationErrorType` | New `Exception` value added | Exhaustive switches must handle new case. |
| 4 | WebCallbackActivity Android Manifest entry | Activity class path moved from global.mattr.mobilecredential.common.webcallback.WebCallbackActivity to global.mattr.mobilecredential.holder.webcallback.WebCallbackActivity | Relevant only if you use web callbacks in the Holder SDK. Update the Android Manifest entry accordingly. |
| 5 | Xcode / Toolchain | Underlying iOS SDK built with Xcode 26.0.0 | Requires Xcode 26.0.0+. Builds will fail on earlier toolchains (e.g. Xcode 16.4). CI environments must be upgraded. |
## New Additions [#new-additions]
### Functions [#functions]
| Function | Description | Platform |
| --------------------------------------- | ---------------------------------------------- | ------------ |
| `getCurrentLogFilePath(options?)` | Returns SDK log file path | All |
| `setDeviceEngagementListener(listener)` | Sets NFC device engagement listener | Android only |
| `removeDeviceEngagementListener()` | Removes NFC device engagement listener | Android only |
| `getNfcConfiguration()` | Returns persisted or default NFC configuration | Android only |
| `setNfcConfiguration(config)` | Persists new NFC configuration | Android only |
### Updated Function Signatures [#updated-function-signatures]
* `generateDeviceKey(options?)` — new optional `authenticationPolicy?: DeviceKeyAuthenticationPolicy`
* `retrieveCredentials(options)` — new optional `authenticationPolicy?: DeviceKeyAuthenticationPolicy`
* `retrieveCredentialsUsingAuthorizationSession(options)` — new optional `authenticationPolicy?: DeviceKeyAuthenticationPolicy`
* `createProximityPresentationSession(options)` — new optional `engagementFromNfc?: boolean`
### New initialize Options [#new-initialize-options]
* `loggerConfiguration?: LoggerConfiguration` — configure SDK logging: logLevel, callbackLogLevel, logDir, callback on log events.
* `dcConfiguration?: DCConfiguration` — enables Digital Credentials API support:
* iOS: `DCConfigurationIOS` (appGroup, supportedDocTypes)
* Android: `DCConfigurationAndroid` (enabled: boolean)
### New Types & Enums [#new-types--enums]
* Logging:
* `LogLevel`
* `LoggerConfiguration`
* NFC:
* `NfcConfiguration`
* `NfcHandoverMode`
* `NfcError`
* NFC Listener type
* Digital Credentials API:
* `DCConfiguration`
* `DCConfigurationIOS`
* `DCConfigurationAndroid`
* `SupportedDocType`
* Device Key Authentication:
* `DeviceKeyAuthenticationPolicy`
* `DeviceKeyAuthenticationType`
* `DeviceKeyAuthenticationInfo`
* `DeprecatedDeviceKeyAuthenticationType`
* Verifier Authentication:
* `VerifierAuthenticationResult`
* `VerifierAuthenticationError`
* `VerifierAuthenticationErrorType`
* `VerifierInfo`
### New Error Values [#new-error-values]
| Location | New value |
| -------------------------------------------------- | ----------------------------------------------------------------------- |
| `MobileCredentialHolderErrorType` | `StorageInitializedInBackground`, `SdkInitialized`, `InvalidInstanceID` |
| `RetrieveCredentialsErrorTypes` | `UnsupportedDeviceKeyAuthenticationPolicy` |
| `ProximityPresentationSessionTerminationErrorType` | `Exception` |
## Minimum Requirements [#minimum-requirements]
**iOS**
* iOS 15+ for core SDK functionality
* iOS 26+ for Digital Credentials API (DC API) features
**Android**
* Android 7 / Nougat / API 24.
* The underlying Android Holder SDK is built using Kotlin 2.0. This adds some intrinsic dependencies into your build tools.
* Kotlin 2.0 is supported from AGP version [8.5](https://developer.android.com/build/kotlin-support).
* AGP 8.5 is supported from Gradle version [8.7](https://developer.android.com/build/releases/about-agp) and Android Studio Koala [2024.1.1](https://developer.android.com/studio/releases).
## Migration Steps [#migration-steps]
### Update `getCredential` calls [#update-getcredential-calls]
The `skipStatusCheck` parameter has been renamed to `fetchUpdatedStatusList` with inverted semantics. When `fetchUpdatedStatusList` is `true` (default), the SDK will fetch the latest status list to ensure up-to-date revocation information. When `false`, it will skip fetching the status list and rely on cached data (when valid). Update all calls accordingly:
```diff
- const credential = await getCredential(id, { skipStatusCheck: true });
+ const credential = await getCredential(id, { fetchUpdatedStatusList: false });
```
| Old Parameter | New Parameter |
| ----------------------------------- | ----------------------------------------- |
| `skipStatusCheck = false` (default) | `fetchUpdatedStatusList = true` (default) |
| `skipStatusCheck = true` | `fetchUpdatedStatusList = false` |
Refer to [Revocation Status check](/docs/holding/credential-claiming-guides/revocation-status-check) for more information.
### Update Error Handling for initialize() [#update-error-handling-for-initialize]
If your code exhaustively handles errors from `initialize()`, add support for the new error types:
* `StorageInitializedInBackground`
* `SdkInitialized`
* `InvalidInstanceID`
### Update ProximityPresentationSessionTerminationErrorType Handling [#update-proximitypresentationsessionterminationerrortype-handling]
If you switch over `ProximityPresentationSessionTerminationErrorType`, add handling for the new `Exception` value. This is a fallback when an unexpected issue occurs during presentation.
```diff
await createProximityPresentationSession({
// ... other options
onSessionTerminated: (error: PresentationSessionTerminationError | null) => {
switch (error?.type) {
// ... other errors
+ case "Exception":
+ // handle error
+ break;
}
},
});
```
### Update Function Calls for Device Key Authentication Policy [#update-function-calls-for-device-key-authentication-policy]
If you use `generateDeviceKey`, `retrieveCredentials`, or `retrieveCredentialsUsingAuthorizationSession`, update calls to support the new optional `authenticationPolicy` parameter.
```diff
const result = await generateDeviceKey({
// ... other options
+ authenticationPolicy: {
+ type: DeviceKeyAuthenticationType.BiometryCurrentSet,
+ },
});
```
### Update Error Handling for RetrieveCredentialsErrorTypes [#update-error-handling-for-retrievecredentialserrortypes]
Add handling for the new `UnsupportedDeviceKeyAuthenticationPolicy` error. This occurs when the requested device key authentication method is not available on the device.
```diff
const result = await retrieveCredentials( ... );
if (result.isErr()) {
switch (result.error.type) {
// ... other errors
+ case "UnsupportedDeviceKeyAuthenticationPolicy":
+ // handle error
+ break;
}
}
```
### Update createProximityPresentationSession Calls [#update-createproximitypresentationsession-calls]
Add the new optional `engagementFromNfc` parameter if you want to use buffered NFC engagement data.
```diff
await createProximityPresentationSession({
// ... other options,
+ engagementFromNfc: true,
});
```
### Update Android Manifest for web callback activity [#update-android-manifest-for-web-callback-activity]
The shared `common` module has been removed and bundled into the underlying Android Holder SDK. If your Holder app uses web callbacks, update the `WebCallbackActivity` entry in your Android Manifest to use the new class path. This is **not** a general package import change.
```diff
-
+
```
# API Reference
URL: /docs/issuance/authorization-code/authentication-provider/api-reference
## Configure an Authentication Provider [#configure-an-authentication-provider]
## Retrieve all Authentication Providers [#retrieve-all-authentication-providers]
## Retrieve an Authentication Provider [#retrieve-an-authentication-provider]
## Update an Authentication Provider [#update-an-authentication-provider]
## Delete an Authentication Provider [#delete-an-authentication-provider]
# How to create an Authentication provider configuration
URL: /docs/issuance/authorization-code/authentication-provider/guide
An [authentication provider](/docs/issuance/authorization-code/authentication-provider/overview) (sometimes referred to
as an Identity Provider, or IdP) is a platform typically used to store and manage user accounts on
behalf of an organization or a service provider. MATTR VII uses the configured authentication
provider to authenticate end users before issuing them credentials via the OID4VCI
[Authorization Code flow](/docs/issuance/authorization-code/overview).
As part of configuring your OID4VCI
[Authorization Code flow](/docs/issuance/authorization-code/overview), you will need to create
an authentication provider configuration on your MATTR VII tenant.
## Prerequisites [#prerequisites]
* An existing Authentication provider that:
* Exposes an OpenID Connect-based interface.
* Is populated with users who can be issued verifiable credentials.
* Is configured with application clients for which you hold the login credentials.
## Overview [#overview]
Creating an Authentication provider configuration comprises the following steps:
1. [Create a MATTR VII Authentication provider configuration](#create-a-mattr-vii-authentication-provider-configuration).
2. [Update your Authentication provider callback URL](#update-your-authentication-provider-callback-url).
### Create a MATTR VII Authentication provider configuration [#create-a-mattr-vii-authentication-provider-configuration]
This step can be performed via the MATTR Portal or an API request.
1. Log in to the [MATTR Portal](https://portal.mattr.global/).
2. In the navigation panel on the left-hand side, expand the **Credential Issuance** menu.
3. Select **Authentication provider**.
4. Insert your Authentication provider application `Domain` in the *Base URL* field. Make sure you prefix it with
`https://`.
5. Insert your Authentication provider application `Client ID` in the *Client ID* field.
6. Insert your Authentication provider application `Client Secret` in the *Client secret* field.
7. Select **Create**.
Make a request of the following structure to create an
[Authentication provider configuration](/docs/issuance/authorization-code/authentication-provider/api-reference#configure-an-authentication-provider):
```http title="Request"
POST /v1/users/authentication-providers
```
```json title="Request body"
{
"url": "https://example.us.auth0.com/",
"scope": ["openid", "profile", "email"],
"clientId": "zH1IGGkRo6q0ofwiNJnlY0bg9fchw3s0",
"clientSecret": "***********************************************************LFRrZ",
"tokenEndpointAuthMethod": "client_secret_post",
"staticRequestParameters": {
"prompt": "login",
"maxAge": 10000
},
"forwardedRequestParameters": ["login_hint"],
"claimsToPersist": []
}
```
* `url` : The URL of your Authentication provider:
* Must be a valid URL.
* Must use the HTTPS protocol.
* Must not be an IP address.
* Must not include query parameters.
* `scope` (*optional*): OpenID scopes to use during authentication. Each scope returns a set of user
attributes which are called claims. Be sure to test that right scopes are added to get all the
information you need. If no scopes are provided, the default scope of \[`openid`,`profile`,`email`]
will be used. If any scopes are provided, `openid` must also be included in the array.
* `clientId` : The client ID of the application client created on your Authentication provider.
* `clientSecret` : The client secret of the application client created on your Authentication
provider.
* `tokenEndpointAuthMethod` : Authentication method for your Authentication provider token endpoint.
The following methods are supported:
* `client_secret_post` : Your credentials are passed as parameters in the request body.
* `client_secret_basic` : Your credentials are passed as a base 64 encoded token.
* `staticRequestParameters` (*optional*): Additional parameters that will be included in the request
to your Authentication provider, and will be identical for every request as defined in your
configuration. An example would be setting the `prompt` to be `login` to let your Authentication
provider know it should show the login page every time. Keys must be strings. Values of top-level
object keys must stringify to less than 1000 characters.
* `forwardedRequestParameters` (*optional*): In contrast to `staticRequestParameters`, you can
provide dynamic parameters that are fetched uniquely for each request to make the user journey
more seamless. Here, you can forward params to your Authentication provider like `login_hint`
which will pass the email of the user starting the flow. Forwarded parameters values are limited
to 1000 characters each.
* `claimsToPersist` (*optional*): List of claims to persist from your Authentication provider to
MATTR VII. If you have attributes from the ID token (e.g. user identifier, email, etc.) that you would
like persisted on MATTR VII, add them to this array. By default this array is empty, meaning no
claims are persisted on MATTR VII.
*Response*
```json title="Response body"
{
"id": "983c0a86-204f-4431-9371-f5a22e506599", // [!code focus]
"redirectUrl": "https://learn.vii.au01.mattr.global/v1/oauth/authentication/callback", // [!code focus]
"url": "https://example.us.auth0.com/",
"scope": ["openid"],
"clientId": "zH1IGGkRo6q0ofwiNJnlY0bg9fchw3s0",
"clientSecret": "***********************************************************LFRrZ", // [!code focus]
"tokenEndpointAuthMethod": "client_secret_post",
"staticRequestParameters": {
"prompt": "login",
"maxAge": 10000
},
"forwardedRequestParameters": ["login_hint"],
"claimsToPersist": []
}
```
* `id` : Unique identifier for the configured Authentication provider. This identifier can be used
to retrieve, update or remove the Authentication provider configuration.
* `redirectUrl` : This is the URL the user should be redirected to upon successful authentication to
continue the OID4VCI [Authorization Code flow](/docs/issuance/authorization-code/overview).
You will add it to to your Authentication provider's allowlist/whitelist in the next step.
* `clientSecret` : Your authentication provider client secret will be masked in the response without
revealing the actual secret. If the secret is less than 20 characters it will be completely
masked, and if it is over 20 only the last 5 characters are revealed.
### Update your Authentication provider callback URL [#update-your-authentication-provider-callback-url]
Add the `redirectUrl` from the [response](#response) above to your Authentication provider's
allowlist/whitelist of callback URLs:
* If you are using [Auth0](https://auth0.com/) as your Authentication provider, this guide shows
[how to add callback URLs](https://auth0.com/docs/authenticate/login/redirect-users-after-login).
* If you are using a different authentication provider, consult their documentation for instructions
on adding callback URLs.
# Authentication provider
URL: /docs/issuance/authorization-code/authentication-provider/overview
## Overview [#overview]
An authentication or identity provider (IdP) is a platform used to store and manage user accounts.
MATTR VII uses auth providers to authenticate users before issuing them credentials as part of the
OID4VCI [Authorization Code flow](/docs/issuance/authorization-code/overview).
To configure a MATTR VII Authentication provider you will need an existing provider that exposes an
OpenID Connect-based interface, populated with users who can be issued verifiable credentials. Your
provider should allow creating application clients, which will be used by MATTR VII to generate the
authentication requests when issuing credentials.
Alongside authentication, you can configure MATTR VII to query your provider for different scopes of
claims that can be retrieved and used as part of the OID4VCI
[Authorization Code flow](/docs/issuance/authorization-code/overview). Available scopes depend
on your provider and its configuration. See
[Auth0 supported scopes](https://auth0.com/docs/get-started/apis/scopes/openid-connect-scopes) as an
example.
Each MATTR VII tenant has a single Authentication provider which is used for all OID4VCI
[Authorization Code flows](/docs/issuance/authorization-code/overview).
## Requirements [#requirements]
You can use any OpenID provider if it supports the following capabilities specified by
[OpenID Connect Core](https://openid.net/specs/openid-connect-core-1_0.html) and
[OpenID Connect Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html):
* Must publish its OpenID Provider configuration at /.well-known/openid-configuration
* Must support Authorization Code flow
* Must support the state parameter
These requirements allow the OID4VCI
[Authorization Code flow](/docs/issuance/authorization-code/overview) to engage with your
Authentication provider and accept an ID Token containing the user claims.
## Ensuring claim availability [#ensuring-claim-availability]
When using the [Authorization Code flow](/docs/issuance/authorization-code/overview), the claims
available for inclusion in the issued credential are determined by data returned by your Authentication provider and (if configured) your Interaction hook and/or Claims source.
A common cause of issuance failures is a mismatch between the claims expected by your
[Credential configuration](/docs/issuance/credential-configuration/overview) and the claims your
Authentication provider actually supplies.
To avoid this, it is important to understand the relationship between scopes and claims, and to
verify that your provider can return the claims your credential configuration requires.
### Scopes and claims [#scopes-and-claims]
In OpenID Connect, **claims** are the individual pieces of information about a user (e.g.
`given_name`, `email`, `birthdate`). **Scopes** are high-level permissions that a client requests
to gain access to categories of claims. The standard claim name in OIDC discovery for date of
birth is `birthdate` (no underscore); you can later map this provider claim into a different
credential claim name (for example, mapping OIDC `birthdate` to a credential claim `birth_date`)
using claim mappings in your credential configuration.
The OIDC specification defines recommended mappings between scopes and claims. For example:
| Scope | Typical claims returned |
| --------- | ---------------------------------------------------------- |
| `openid` | `sub` |
| `profile` | `name`, `given_name`, `family_name`, `nickname`, `picture` |
| `email` | `email`, `email_verified` |
| `address` | `address` |
| `phone` | `phone_number`, `phone_number_verified` |
However, these mappings are not guaranteed. Your Authentication provider ultimately decides which
claims are returned based on:
* The scopes requested by the client.
* The provider's own configuration and custom mappings.
* Whether the user's profile actually contains values for those claims.
For example, requesting the `profile` scope does not guarantee that `given_name` will be returned.
The user's profile must have a value for that field, and the provider must be configured to include
it in the response.
### Checking your provider's supported claims [#checking-your-providers-supported-claims]
Every OIDC-compliant provider publishes a discovery document at
`/.well-known/openid-configuration`. This document includes two key fields for understanding
claim availability:
* `claims_supported`: Typically lists the specific claims the provider can return. This list may
not be exhaustive, and additional claims may still be returned depending on your provider's
configuration and custom mappings.
* `scopes_supported`: Typically lists the scopes the provider accepts. Scopes that are not listed
here may be ignored or rejected, but behaviour can vary across providers, so you should validate
supported scopes by testing your integration (for example, by inspecting issued tokens and
UserInfo responses).
For example, a provider's discovery document might list:
```json title="Excerpt from /.well-known/openid-configuration"
{
"claims_supported": [
"sub", "name", "given_name", "family_name",
"email", "email_verified", "picture"
],
"scopes_supported": [
"openid", "profile", "email", "address"
]
}
```
In this case, standard identity claims like `given_name` and `email` are supported. However, the
standard OIDC claim `birthdate` is **not** listed in `claims_supported`, so the provider will not
return it unless it is explicitly configured as a supported claim. Custom claim names (for example,
`employee_id`) are provider-specific and will not appear in `claims_supported` unless your provider
is configured to expose them.
A claim being listed in `claims_supported` does not guarantee it will always be returned. It
means the provider **can** return it — provided the right scopes are requested and the user's
profile contains a value for that claim.
### Verifying claims for your credential configuration [#verifying-claims-for-your-credential-configuration]
Before creating a credential configuration that relies on claims from your Authentication provider,
follow these steps:
1. **Review your provider's discovery document**: Fetch the `/.well-known/openid-configuration`
endpoint and check `claims_supported` against the claims your credential configuration requires.
2. **Confirm scope configuration**: Ensure the MATTR VII Authentication provider configuration
requests the scopes needed to access those claims (e.g. `profile` for `given_name`, `email` for
`email`).
3. **Check user profile data**: Verify that the user accounts in your provider have values
populated for the required claims. Even supported claims will not be returned if the user's
profile is missing that data.
4. **Handle unsupported provider claims and credential-only fields**: For claims that are not
listed in `claims_supported` but you still want as OIDC claims (e.g. the standard `birthdate`
claim), or for credential-specific fields that are not expected to come from your Authentication
provider (e.g. an `expiry_date` field on the credential), consider the following options:
* For provider claims, add them to your provider as custom claims using provider-specific
features (e.g. Auth0 Actions or Rules) so they can appear in ID tokens or the UserInfo
response.
* For credential-only fields, use a [Claims source](/docs/issuance/claims-source/overview) or an
Interaction hook to supply additional attributes from your own data, rather than expecting
them from the Authentication provider.
* Set a `defaultValue` in the credential configuration so issuance can succeed even when the
claim is not provided by the provider or any other source.
If a claim is marked as `required` in your
[Credential configuration](/docs/issuance/credential-configuration/overview#claims-mapping) and
is not returned by your Authentication provider (or any other configured claims source), and no
`defaultValue` is set, credential issuance will fail.
## Request parameters [#request-parameters]
You can configure MATTR VII to include request parameters when the holder is redirected to the
Authentication provider:
* Static request parameters are included in the request to your IdP, and will be identical for every
request as defined in your configuration. An example would be setting the `prompt` to be `login`
to let your Authentication provider know it should show the login page every time.
* Keys must be strings.
* Values of top-level object keys must stringify to less than 1000 characters.
* Dynamic request parameters are fetched uniquely for each request to make the user journey more
seamless. Here, you can forward params to your Authentication provider like `login_hint` which
will pass the email of the user starting the flow.
* Dynamic parameters values are limited to 1000 characters each.
## Persisting claims [#persisting-claims]
Upon successful authentication the provider responds with an ID token. You can configure MATTR VII
to persist selected claims on your tenant.
By default no claims are persisted. It is highly recommended to take privacy considerations into
account before configuring MATTR VII to persist any claims.
# API Reference
URL: /docs/issuance/authorization-code/interaction-hook/api-reference
## Create an Interaction hook [#create-an-interaction-hook]
## Retrieve Interaction hook [#retrieve-interaction-hook]
# How to set up an Interaction hook
URL: /docs/issuance/authorization-code/interaction-hook/guide
An [Interaction hook](/docs/issuance/authorization-code/interaction-hook/overview) enables you to
redirect users to a custom component during the OID4VCI
[Authorization Code flow](/docs/issuance/authorization-code/overview). This redirect happens after
the user authenticates but before the credential is issued, allowing you to introduce custom
interactions such as collecting additional information, performing MFA or biometric checks, or
presenting terms of service.
This guide walks you through the steps required to configure and integrate an Interaction hook into
your credential issuance workflow.
## Prerequisites [#prerequisites]
* A configured OID4VCI [Authorization Code flow](/docs/issuance/authorization-code/overview) with an
[Authentication provider](/docs/issuance/authorization-code/authentication-provider/overview).
* A publicly accessible web or native application to serve as your Interaction hook component.
We recommend using a web application as your Interaction hook component, as this is more
compatible with most scenarios.
## Overview [#overview]
Setting up an Interaction hook comprises the following steps:
1. [Configure the Interaction hook on your tenant](#configure-the-interaction-hook).
2. [Verify the Interaction hook redirect](#verify-the-interaction-hook-redirect).
3. [Process the interaction and redirect back to MATTR VII](#redirect-back-to-mattr-vii).
### Configure the Interaction hook [#configure-the-interaction-hook]
Configure your MATTR VII tenant with the URL of your Interaction hook component. This tells MATTR
VII where to redirect users after they authenticate.
Each tenant can only have a single Interaction hook configured. If your workflow requires
multiple custom interactions, they must be implemented within the same component.
1. Sign in to the [MATTR Portal](https://portal.mattr.global/).
2. In the left navigation, expand **Credential Issuance**.
3. Select **Interaction hook**.
4. In **Redirect URL post user completion**, enter the publicly accessible URL of your Interaction
hook component.
5. Optionally configure the following:
* **Claims**: Select which user claims from the Authentication Provider should be included in the
session token sent to your component.
* **Session timeout**: Set the session duration (in seconds) for the interaction. If not set, the
default environment session duration applies.
6. Set **Status** to **Enabled**.
7. Select **Update** to save the configuration.
8. Copy the displayed **Secret**. You will need this to verify and sign JWTs.
Make a PUT request to
[configure the Interaction hook](/docs/issuance/authorization-code/interaction-hook/api-reference#create-an-interaction-hook):
```http title="Request"
PUT /v1/openid/configuration
```
```json title="Request body"
{
"interactionHook": {
"url": "https://your-interaction-hook.example.com",
"claims": ["first_name", "last_name", "email"],
"sessionTimeoutInSec": 1200,
"disabled": false
}
}
```
* `url` : The publicly accessible URL of your Interaction hook component. Must use HTTPS, must not
be an IP address, and must not include query parameters.
* `claims` : An array of user attribute names from the Authentication Provider that will be included
in the session token sent to your component. Claims can only contain alphanumeric characters, `_`,
or `-`. If empty or not defined, no claims are sent.
* `sessionTimeoutInSec` : The session duration in seconds (minimum: 300, maximum: 7200). Once a
session expires, the user is shown an error when redirected. If not specified, the default
environment session duration applies.
* `disabled` : Set to `false` to enable the Interaction hook. When `true` (default), the hook is
disabled and users are not redirected.
*Response*
```json title="Response body"
{
"interactionHook": {
"url": "https://your-interaction-hook.example.com",
"claims": ["first_name", "last_name", "email"],
"sessionTimeoutInSec": 1200,
"disabled": false,
"secret": "dGtUrijBOT6UUJ8JO4kAFyGfhahDlVVeIk/sPbWTa7c=" // [!code focus]
}
}
```
* `secret` : A Base64-encoded 32-byte HMAC secret. Store this securely — you will use it to verify
incoming JWTs and sign response JWTs.
Once the Interaction hook is enabled, any OID4VCI Authorization Code issuance workflow on your tenant
will redirect the user to your component after authentication.
### Verify the Interaction hook redirect [#verify-the-interaction-hook-redirect]
When MATTR VII redirects the user to your component, it appends a signed JWT as a `session_token`
query parameter:
```
https://your-interaction-hook.example.com?session_token=eyJhbGciOiJIUzI1NiIs...
```
Your component must verify this JWT using the `secret` obtained during configuration to confirm that
the request originates from MATTR VII:
1. Extract the `session_token` query parameter from the incoming request URL.
2. Verify the JWT signature using the Base64-decoded `secret` with the HS256 algorithm.
3. Validate the following JWT claims:
* `iss` : Must match your MATTR VII tenant URL.
* `aud` : Must match your Interaction hook URL.
* `exp` : Must not be expired.
4. If verification fails, reject the request.
**Decoded JWT payload structure:**
```json title="Example decoded session_token payload"
{
"state": "hJvfiSp3eEGybd-KmL8ja",
"scopes": ["ldp_vc:CourseCredential"],
"claims": {
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@example.com"
},
"authenticationProvider": {
"url": "https://your-idp.example.com",
"subjectId": "auth0|123456789"
},
"redirectUrl": "https://your-tenant.vii.mattr.global/v1/oauth/interaction/hJvfiSp3eEGybd-KmL8ja/interactionhook/callback",
"sub": "a44a7f92-c61e-48a0-88b6-863eeeb58394",
"aud": "https://your-interaction-hook.example.com",
"iss": "https://your-tenant.vii.mattr.global",
"iat": 1673910963,
"exp": 1673911263
}
```
* `state`: Unique session identifier. Must be included in your response JWT.
* `claims`: User claims from the Authentication Provider (based on your configuration).
* `redirectUrl`: The URL to redirect the user back to after the interaction hook custom component completes.
* `authenticationProvider.url`: The Authentication Provider URL.
* `authenticationProvider.subjectId`: The user's subject identifier with the Authentication Provider.
The session token is passed in the URL and is subject to browser URL character limits. Avoid
transferring large data (such as images) to or from the Interaction hook component.
### Redirect back to MATTR VII [#redirect-back-to-mattr-vii]
Once the user completes the interaction, your component must redirect them back to MATTR VII to
continue the issuance flow. This involves signing a new JWT and including it as a query parameter.
**Steps to complete the redirect:**
1. **Sign a response JWT** using the same `secret` (Base64-decoded) with the HS256 algorithm:
```json title="Response JWT payload"
{
"iss": "https://your-interaction-hook.example.com",
"aud": "https://your-tenant.vii.mattr.global",
"state": "hJvfiSp3eEGybd-KmL8ja",
"claims": {
"preferred_name": "Jane Smith"
},
"claimsToPersist": ["preferred_name"],
"iat": 1673911000,
"exp": 1673911060
}
```
* `iss` (required): Your Interaction hook URL.
* `aud` (required): Your MATTR VII tenant URL.
* `state` (required): The `state` value from the original session token. This links the response to the correct issuance session.
* `claims` (optional): An object containing claims to include in the issued credential. These values can override claims from the Authentication Provider.
* `claimsToPersist` (optional): An array of claim names from `claims` that should be persisted on the MATTR VII tenant for future use. If empty or omitted, no claims are persisted.
* `iat` (required): Issued-at timestamp.
* `exp` (required): Expiration timestamp. Use a short window (e.g. 1 minute) to minimize the risk of token replay.
2. **Redirect the user** to the `redirectUrl` from the original session token, appending the signed
JWT as a `session_token` query parameter:
```
https://your-tenant.vii.mattr.global/v1/oauth/interaction/hJvfiSp3eEGybd-KmL8ja/interactionhook/callback?session_token=eyJhbGciOiJIUzI1NiIs...
```
MATTR VII validates the response JWT and uses the returned claims according to your
[Credential configuration](/docs/issuance/credential-configuration/overview) claim mappings.
#### Handling errors [#handling-errors]
If your component determines that the user should not proceed with credential issuance (for example,
they failed a biometrics check), you can signal an error by including an `error` object in the
response JWT payload instead of `claims`:
```json title="Error response JWT payload"
{
"iss": "https://your-interaction-hook.example.com",
"aud": "https://your-tenant.vii.mattr.global",
"state": "hJvfiSp3eEGybd-KmL8ja",
"error": {
"message": "Biometric verification failed"
},
"iat": 1673911000,
"exp": 1673911060
}
```
The `error` object must include a `message` field (string). You can include additional fields
alongside `message`, but only `message` is used by MATTR VII.
**What happens when an error is returned:**
1. MATTR VII terminates the issuance session.
2. The user is redirected back to the wallet application with the following error parameters appended
to the redirect URI:
* `error`: `access_denied`
* `error_description`: `Invalid interaction request`
3. The `error.message` from your JWT is included in the `OPENID_INTERACTION_HOOK_FAIL`
[analytics event](/docs/platform-management/analytics/overview). This can be used for debugging
and monitoring, but is not visible to the end user.
Do not include personally identifiable information (PII) in the `error.message` field, as it
is captured in analytics events and platform logs.
## Best practices [#best-practices]
* **Secure your component:** Since the Interaction hook component must be publicly accessible, use
the JWT verification mechanism to protect against unauthorized access.
* **Keep sessions short:** Set `sessionTimeoutInSec` to an appropriate duration for your use case.
Shorter sessions reduce the window for potential misuse.
* **Use short-lived response JWTs:** Set the `exp` claim in your response JWT to a short window
(e.g. 1 minute) to minimize the risk of token replay.
* **Only persist necessary claims:** Use `claimsToPersist` selectively. Only persist claims that you
need for future interactions to minimize data storage.
* **Handle expiry gracefully:** If the session expires before the user completes the interaction,
display a user-friendly message and guide them to restart the issuance flow.
* **Combine interactions:** If you need multiple steps (e.g. biometric check followed by terms
acceptance), build them into a single Interaction hook component.
## What's next? [#whats-next]
* Follow the [Interaction hook tutorial](/docs/issuance/authorization-code/interaction-hook/tutorial)
to see a step-by-step walkthrough using a sample application.
* Review the
[Interaction hook API reference](/docs/issuance/authorization-code/interaction-hook/api-reference)
for full request and response schemas.
# Interaction hook
URL: /docs/issuance/authorization-code/interaction-hook/overview
Many issuance workflows require the issuer to perform custom interactions with the user. This could
include gathering more information, introducing additional authentication steps (e.g. MFA or biometric checks) or communicating the terms of
service.
To facilitate this you can configure MATTR VII to invoke an interaction hook which will redirect the
user to a custom component. This redirect happens after the user is authenticated but before the
credential is issued. The redirect can include any user claims retrieved from the Authentication
provider, and these can be consumed and used by the interaction hook component.
Interactions hooks are only available for the OID4VCI [Authorization Code
flow](/docs/issuance/authorization-code/overview)
The interaction hook component must be publicly available, and thus requires access controls to
protect it from unauthorized access. MATTR VII interaction hook integration supports a JWT
HS256-based authorization scheme to enable this.
When MATTR VII redirects the end user to your interaction hook component, it will include a JWT
object as a `session_token` query parameter. Your interaction hook component should verify this JWT
object using the interaction hook `secret` to make sure that the request is legitimate. This
`secret` can be obtained when you [configure an interaction hook](/docs/issuance/authorization-code/interaction-hook/tutorial).
If the verification fails, the request should be rejected.
This token is passed in the URL, and is subject to the max URL character limits. Limits differ
amongst browsers but 2000 characters should meet most browsers requirements. This means that you
shouldn’t transfer big files (for example images) to and/or from the Interaction hook component.
Once the end user completes the interaction hook journey, they need to be redirected back to MATTR
VII to complete the issuance workflow. The redirect URL can be found in the session token JWT
payload, and the issuer must generate and sign a new JWT token using the same `secret`, and include
it as a query parameter with the redirect.
Any claims returned by the interaction hook component can be included in the issued credential.
However, they are not persisted on your tenant unless the interaction hook is explicitly configured
to do.
Upon the successful completion of the interaction hook, your custom component will redirect the user
back to their digital wallet to complete the credential issuance flow.
If your interaction hook component determines that the user should not be allowed to continue with
the issuance journey (for example, they’ve failed a biometrics test), you can return an error in
your signed JWT which will signal to the issuance journey that it should fail.
## Best practices [#best-practices]
* Interaction hook components can be either a web or native application. We recommend using web
applications as they are more compatible with most scenarios.
* You can combine several custom interactions as part of the issuance workflow by building them into
a single Interaction hook component.
# Learn how to integrate an interaction hook into an OID4VCI workflow
URL: /docs/issuance/authorization-code/interaction-hook/tutorial
## Introduction [#introduction]
In an OID4VCI [Authorization Code flow](/docs/issuance/authorization-code/overview) you can enhance the credential issuance
process with custom interactions by configuring an
[Interaction hook](/docs/issuance/authorization-code/interaction-hook/overview). This could include gathering additional
information, introducing extra authentication steps, or communicating the terms of service. This
allows you to create a more tailored and engaging credential issuance experience.
Interaction hooks are only available for the OID4VCI [Authorization Code
flow](/docs/issuance/authorization-code/overview).
This tutorial focuses on guiding you through the process of setting up an
[OID4VCI workflow](/docs/issuance/authorization-code/overview) that redirects the user to a custom interaction where
they can provide additional details that will be used in the issued credential.
## Prerequisites [#prerequisites]
* Ensure you have completed the OID4VCI Authorization Code [tutorial](/docs/issuance/authorization-code/tutorial), as this tutorial builds
upon it. In particular, ensure that you have the QR code generated in the
[Create a credential offer step](/docs/issuance/authorization-code/tutorial#create-a-credential-offer).
* To test locally you will need to expose your local interaction hook to the internet. You can do
this by setting up a [free ngrok account](https://ngrok.com/), using a cloudflare tunnel or using
your own solution. For this tutorial we will be using ngrok. Sign up for a free account at
[ngrok.com](https://ngrok.com/).
We recommend using the MATTR VII [Postman
collection](/docs/api-reference#postman-collection) in this
tutorial. While this isn't an explicit prerequisite it can really speed things up.
## Tutorial overview [#tutorial-overview]
The current tutorial builds upon the OID4VCI Authorization Code [Tutorial](/docs/issuance/authorization-code/tutorial) by adding the following steps:
1. **Set up a sample web application:** Deploy a sample Next.js web app that acts as the custom
interaction component in the issuance flow. In this example, the app lets users update their
preferred name and pronoun, which will be included in the issued credential. You can extend this
app to collect any additional information or add custom steps such as extra authentication,
biometrics checks or liveness checks, as needed for your workflow.
2. **Integrate the Interaction hook with MATTR VII:** Configure MATTR VII to redirect the user to
the Interaction hook as part of the issuance flow, and then handle any information returned by
the Interaction hook as the user is redirected back to the issuance flow.
The following diagram illustrates how an interaction hook fits into the OID4VCI Authorization Code issuance workflow:
The workflow proceeds as follows:
1. **Accept credential offer:** The wallet accepts the credential offer and initiates the OID4VCI [Authorization Code flow](/docs/issuance/authorization-code/overview), redirecting the user to the Authentication Provider.
2. **Authenticate user:** The Authentication Provider authenticates the user and returns any associated claims to MATTR VII.
3. **Redirect to interaction hook:** MATTR VII redirects the user to the configured Interaction Hook component, including a signed `session_token` JWT as a query parameter. This token carries context such as user claims from the Authentication Provider and the redirect URL to return to after the interaction.
4. **Verify and interact:** The Interaction Hook component verifies the `session_token` using the shared `secret` to confirm the request is legitimate. The user then completes the custom interaction — this could be providing additional information, completing an MFA or biometrics check, or accepting terms of service.
5. **Return to MATTR VII:** Once the interaction is complete, the component signs a new JWT using the same `secret` — optionally including any additional claims — and redirects the user back to MATTR VII via the URL found in the session token payload.
6. **Map and issue:** MATTR VII combines claims from the Authentication Provider and the Interaction Hook, maps them according to the [credential configuration](/docs/issuance/credential-configuration/overview), and issues the signed credential to the wallet.
## Tutorial steps [#tutorial-steps]
### Start a ngrok tunnel on your local machine [#start-a-ngrok-tunnel-on-your-local-machine]
1. Start an ngrok tunnel in a new terminal window:
```bash title="Start a ngrok tunnel"
ngrok http http://localhost:3000
```
Your terminal window should show the tunnel details:
2. Make note of the `Forwarding` value, we will use it in the next step.
### Configure an Interaction hook on your MATTR VII tenant [#configure-an-interaction-hook-on-your-mattr-vii-tenant]
This step can be performed via the MATTR Portal or an API request.
1. Sign in to the [MATTR Portal](https://portal.mattr.global/).
2. In the left navigation, expand **Credential Issuance**.
3. Select **Interaction hook**.
4. In **Redirect URL post user completion**, enter the `Forwarding` URL displayed in the terminal after starting the ngrok tunnel. This URL will be used to redirect the user to your custom interaction component during the OID4VCI workflow.
5. Set **Status** to **Enabled**.
6. Select **Update** to save the configuration.
7. Copy the displayed *Secret*. You will need it to [verify the interaction hook redirect](#verify-the-interaction-hook-redirect-jwt).
Make a request of the following structure to
[configure a new Interaction hook](/docs/issuance/authorization-code/interaction-hook/api-reference#create-an-interaction-hook):
```http title="Request"
PUT /v1/openid/configuration
```
```json title="Request body"
{
"interactionHook": {
"url": "https://7658-3-24-140-84.ngrok-free.app",
"disabled": false
}
}
```
* `url` : Replace with the `Forwarding` URL displayed in the terminal after starting the ngrok
tunnel. This URL will be used to redirect the user to your custom interaction component during the
OID4VCI workflow.
* `disabled` : Set to `false` to enable the Interaction hook. If set to `true` (or if `disabled` is
not provided), the Interaction hook will not be used during the OID4VCI workflow.
*Response*
```json title="Response body"
{
"interactionHook": {
"url": "https://7658-3-24-140-84.ngrok-free.app",
"disabled": false,
"secret": "uwIkhw************************************" // [!code focus]
}
}
```
* `secret` : The secret is a 32-byte array, provided as a Base64-encoded string. You will use this
secret later in the tutorial.
Once the Interaction hook is configured and enabled (`disabled` is set to `false`), any OID4VCI Authorization Code
issuance workflow will redirect the user to the Interaction hook component after they have
authenticated.
Any tenant can only have a single Interaction hook configured. You can combine several
custom interactions as part of the issuance workflow by building them into a single Interaction
hook component.
Currently there is nothing running on the Interaction hook URL, so the next step is to set up a
local web application that the user can interact with.
### Setup a local web application as an Interaction hook component [#setup-a-local-web-application-as-an-interaction-hook-component]
Next, you will set up a local Interaction hook component that allows the user to update their
preferred name. MATTR VII will use this updated name in the issued credential. This component is a
Next.js application that simulates a custom interaction, which the user is redirected to during the
issuance flow.
We are using Next.js because it allows us to show both the server side and client side
responsibilities of the web app.
Interaction hook components can be either a web or native application. We recommend using web
applications as they are more compatible with most scenarios.
Perform the following steps to setup the Interaction hook application:
1. Clone the [MATTR Sample Apps repo](https://github.com/mattrglobal/sample-apps) to your machine
and navigate to the `interaction-hook-app` folder.
2. Rename the `.env-example` file to `.env`.
3. Open the `.env` file in a text editor and update the following variables:
* `INTERACTION_HOOK_SECRET`: Replace this with the `secret` value returned in the response when
you configured the Interaction hook in the previous step.
* `APP_URL`: Replace this with the `Forwarding` URL displayed in the terminal after starting the
ngrok tunnel. This URL will be used to redirect the user to your custom interaction component.
* `ISSUER_TENANT_URL`: Replace this with the URL of your MATTR VII tenant, for example:
`https://learn.vii.au01.mattr.global`.
4. Install and start the app by running the following command in the terminal:
```bash title="Install and run the app"
npm install
npm run dev
```
Your Interaction hook component is now set up and ready to be used in the OID4VCI workflow. Let's
test that the interaction works as expected.
### Test the workflow [#test-the-workflow]
To test this workflow you will use the credential offer created as part of the OID4VCI Authorization
Code tutorial.
1. Open the GO hold example app.
2. Select **Scan**.
3. Scan the QR code generated when you [created the credential offer](/docs/issuance/authorization-code/tutorial#create-a-credential-offer).
4. Review the credential offer and select **Accept**.
5. Complete authentication.
6. Following authentication you will be redirected to the Interaction hook component.
7. Update your preferred name and select **Submit** (make sure you use a different name than the one
you setup for your Auth0 user to see the difference in the issued credential). This will redirect
you back to the OID4VCI workflow.
8. You will be redirected back to the OID4VCI workflow, where you can review the credential and
select **Accept**.
9. The new credential will be issued using the name you provided in the Interaction hook component,
replacing the original value retrieved from the authentication provider.
Now that the workflow is working, let's break down our Interaction hook application and understand
how it is integrated into the OID4VCI workflow.
## Breaking down the Interaction hook component [#breaking-down-the-interaction-hook-component]
The sample interaction hook application provided in this tutorial is intentionally simple. While it
contains some basic features that are not covered here, we will focus on the essential
functionalities required for any Interaction hook component integrated with an OID4VCI Authorization
Code flow:
1. **Use the Interaction hook JWT decoded payload**: The application can extract different claims
from the decoded JWT payload and use them in the interaction.
2. **Sign the Interaction hook response JWT and redirect back to MATTR VII**: Once the user
completes the interaction, the application must generate and sign a new JWT token and include it
as a query parameter when redirecting the user back to MATTR VII.
### Use the Interaction hook JWT decoded payload [#use-the-interaction-hook-jwt-decoded-payload]
MATTR VII redirects users to your Interaction hook component with a JWT object passed as a
`session_token` query parameter, as shown in the following example:
`https://7658-3-24-140-84.ngrok-free.app?session_token=ey...`
Your application can decode this JWT object, extract different claims from the decoded payload and
use them in the interaction. The following code snippet shows the structure of the decoded JWT
payload retrieved from the Interaction hook configured above:
```json title="Decoded example JWT payload"
{
"state": "hJvfiSp3eEGybd-KmL8ja",
"scopes": ["ldp_vc:CourseCredential"],
"claims": {
"name": "John Doe",
"email": "example@example.com"
},
"authenticationProvider": {
"url": "https://myidentityprovider.auth0.com",
"subjectId": "user|123456789"
},
"redirectUrl": "https://learn.vii.au01.mattr.global/v1/oauth/interaction/hJvfiSp3eEGybd-KmL8ja/interactionhook/callback",
"sub": "a44a7f92-c61e-48a0-88b6-863eeeb58394",
"aud": "",
"iss": "https://learn.vii.au01.mattr.global",
"iat": 1673910963,
"exp": 1673911263
}
```
* `state` : A unique value associated with each Interaction hook session. You will use it in the
next step when signing the response JWT so that MATTR VII can match the response to the correct
credential issuance flow.
* `scopes` : Scopes retrieved from user authentication workflow.
* `claims` : User claims defined when you
[configured your interaction hook](#configure-a-mattr-vii-interaction-hook).
* `authenticationProvider` : A provider that the user has authenticated with.
* `url` : URL of the Authentication provider.
* `subjectId` : Subject Identifier of the end user with this Authentication provider.
* `redirectUrl` : URL to redirect to when users complete the Interaction hook journey.
* `sub` : User identifier in MATTR VII.
* `aud` : Interaction hook URL.
* `iss` : Your MATTR VII tenant.
* `iat` : Issued at (Epoch Unix timestamp)
* `exp` : Expires at (Epoch Unix timestamp).
Now let's take a look at how the sample application uses the `first_name` and `last_name` claims
from the JWT payload to allow the user to update their preferred name:
```tsx title="src/app/page.tsx"
import { decodeJwt } from 'jose'
import { useRouter, useSearchParams } from 'next/navigation'
import { useCallback, useMemo } from 'react'
// Optional validation for claims
// const passedClaims = z.object...
export default function Home() {
const router = useRouter()
// Get the session token from the URL
const searchParams = useSearchParams()
const sessionToken = searchParams.get('session_token')
// Decode the JWT to use the claims
const decodedToken = useMemo(() => {
if (!sessionToken) return null
try {
return decodeJwt(sessionToken)
} catch (err) {
return null
}
}, [sessionToken])
// Optional: Use ZOD to parse the claims from the decoded JWT
// const claims = useMemo(() => {
// if (!decodedToken) return null;
// try {
// const parsed = passedClaims.parse(decodedToken.claims);
// return parsed;
// } catch (err) {
// return null;
// }
// }, [decodedToken]);
const handleSubmit = useCallback(async () => {
// Perform any last minute validation, etc.
try {
// Form Submission Handler - Step 4.5: Send data to backend API
const response = await fetch('/api/interaction-hook', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
token: sessionToken,
data: {
// Pass your updated claims here
}
})
})
// Handle any errors...
const result = await response.json()
// The redirect URL contains the signed JWT response
router.push(result.redirect)
} catch (err) {
console.error(err)
}
}, [sessionToken])
return
}
```
1. The decoded JWT payload is passed into the rendered React page and the `name` claim is used to
pre-fill a form.
2. The user can update their name and submit the form, which triggers the next step in which the
application signs a new JWT and redirects the user back to MATTR VII.
### Sign the Interaction hook response JWT and redirect back to MATTR VII [#sign-the-interaction-hook-response-jwt-and-redirect-back-to-mattr-vii]
Once the user completes the interaction (in our example this is when they submit the form), your
application must sign a new JWT using the secret provided by MATTR VII and redirect the user back to
MATTR VII. This allows the issuance flow to continue with the updated information.
Your application backend must first ensure that the original request is legitimate and originates
from MATTR VII by verifying the JWT object using the interaction hook `secret` provided in the
response when you
[configured the Interaction hook](#configure-an-interaction-hook-on-your-mattr-vii-tenant).
Here's how the sample application generates and signs the response JWT:
```ts title="src/app/api/interaction-hook/route.ts"
import { jwtVerify, SignJWT, type JWTVerifyResult } from 'jose'
// Optional request schema
// const requestSchema = z.object...
export async function POST(request: Request) {
try {
// Parse request from client
const body = await request.json()
// Optional: use ZOD to parse & validate the request
// const { token, data } = requestSchema.parse(body);
const { token, data } = body
// Load environment variables
const secret = process.env.INTERACTION_HOOK_SECRET
const issuerUrl = process.env.ISSUER_TENANT_URL
const appUrl = process.env.APP_URL
// Error handling...
// MATTR VII provides the secret as base64-encoded when you create the interaction hook
const secretBuffer = Buffer.from(secret, 'base64')
// Verify the JWT from MATTR VII
let verifiedJwt: JWTVerifyResult
try {
verifiedJwt = await jwtVerify(token, secretBuffer, {
issuer: issuerUrl,
audience: appUrl
})
} catch (verifyError: unknown) {
return Response.json({ error: 'Invalid session token' }, { status: 401 })
}
// Sign new JWT with update claims
const responseJwt = await new SignJWT({
iss: appUrl, // Issuer: Your app
aud: issuerUrl, // Audience: MATTR VII tenant
state: verifiedJwt.payload.state, // Session state from original request
// Custom claims to be added to the credential
claims: {
name: data.name,
pronouns: data.pronouns
},
// Claims to persist in MATTR VII (empty in this example)
// You could store data here for future interactions
claimsToPersist: []
})
.setProtectedHeader({ alg: 'HS256', typ: 'JWT' })
.setIssuedAt()
.setExpirationTime('1m') // JWT expires in 1 minute
.sign(secretBuffer)
// Construct return URL for the frontend to redirect to
const redirectUrl = `${verifiedJwt.payload.redirectUrl}?session_token=${responseJwt}`
// return response to the client
return Response.json({
redirect: redirectUrl,
// Include debug info in development only
...(process.env.NODE_ENV === 'development' && {
debug: {
claimsProcessed: Object.keys(data),
jwtCreated: true
}
})
})
} catch (error) {
// Handle any errors...
}
}
```
1. The handler verifies the original `session_token` and extracts the `state`, `redirectUrl`, and
other identifiers.
2. It signs a new JWT (`interaction_token`) with the updated `preferred_name` claim. The
`interaction_token` must include the `state` value from the original session to ensure that MATTR
VII can match the response to the correct credential issuance flow.
3. The response is a 302 redirect back to the `redirectUrl`, with the signed JWT included as a query
parameter.
4. MATTR VII will validate this signed JWT and continue the issuance process using the data you've
included in the response. This means that you can use the new `claims.preferred_name` value in
the issued credential.
## What's next? [#whats-next]
Check out more resources on MATTR Learn that will enable you to:
* Configure a [Claims source](/docs/issuance/claims-source/tutorial) to retrieve data from
compatible data sources and use it in the issued credential.
* Apply branding to issued credentials as part of creating a
[Credential configuration](/docs/issuance/credential-configuration/guide).
# Document signers
URL: /docs/issuance/certificates/api-reference/document-signers
## Create a Document signer [#create-a-document-signer]
## Revoke a Document signer [#revoke-a-document-signer]
## Retrieve all Document signers [#retrieve-all-document-signers]
## Retrieve a Document signer [#retrieve-a-document-signer]
## Update a Document signer [#update-a-document-signer]
## Delete a Document signer [#delete-a-document-signer]
# IACA
URL: /docs/issuance/certificates/api-reference/iaca
## Create an IACA [#create-an-iaca]
## Retrieve all IACAs [#retrieve-all-iacas]
## Retrieve an IACA [#retrieve-an-iaca]
## Update an IACA [#update-an-iaca]
## Delete an IACA [#delete-an-iaca]
# Issuer metadata
URL: /docs/issuance/certificates/api-reference/issuer-metadata
## Retrieve issuer metadata [#retrieve-issuer-metadata]
# CWT Credentials
URL: /docs/issuance/credential-configuration/api-reference/cwt
## Create a CWT Credential configuration [#create-a-cwt-credential-configuration]
## Retrieve all CWT Credential configurations [#retrieve-all-cwt-credential-configurations]
## Retrieve a CWT Credential configuration [#retrieve-a-cwt-credential-configuration]
## Update a CWT Credential configuration [#update-a-cwt-credential-configuration]
## Delete a CWT Credential configuration [#delete-a-cwt-credential-configuration]
# mDocs Credentials
URL: /docs/issuance/credential-configuration/api-reference/mdocs
## Create an mDocs Credential configuration [#create-an-mdocs-credential-configuration]
## Retrieve all mDocs Credential configurations [#retrieve-all-mdocs-credential-configurations]
## Retrieve an mDocs Credential configuration [#retrieve-an-mdocs-credential-configuration]
## Update an mDocs Credential configuration [#update-an-mdocs-credential-configuration]
## Delete an mDocs Credential configuration [#delete-an-mdocs-credential-configuration]
# Semantic CWT Credentials
URL: /docs/issuance/credential-configuration/api-reference/semantic-cwt
## Create a Semantic CWT Credential configuration [#create-a-semantic-cwt-credential-configuration]
## Retrieve all Semantic CWT Credential configurations [#retrieve-all-semantic-cwt-credential-configurations]
## Retrieve a Semantic CWT Credential configuration [#retrieve-a-semantic-cwt-credential-configuration]
## Update a Semantic CWT Credential configuration [#update-a-semantic-cwt-credential-configuration]
## Delete a Semantic CWT Credential configuration [#delete-a-semantic-cwt-credential-configuration]
# Revocation lists
URL: /docs/issuance/revocation/api-reference/cwt-revocation-lists
## Retrieve all CWT credentials revocation lists [#retrieve-all-cwt-credentials-revocation-lists]
## Retrieve a CWT credential revocation list [#retrieve-a-cwt-credential-revocation-list]
# Status update
URL: /docs/issuance/revocation/api-reference/cwt-status-update
## Update CWT credential status [#update-cwt-credential-status]
## Retrieve CWT credential status [#retrieve-cwt-credential-status]
# Status List Configuration
URL: /docs/issuance/revocation/api-reference/mdocs-status-list-configuration
## Create a Status List configuration [#create-a-status-list-configuration]
## Retrieve all Status List configurations [#retrieve-all-status-list-configurations]
## Retrieve a Status List configuration [#retrieve-a-status-list-configuration]
## Update a Status List configuration [#update-a-status-list-configuration]
## Delete a Status List configuration [#delete-a-status-list-configuration]
# Status List retrieval
URL: /docs/issuance/revocation/api-reference/mdocs-status-list-retrieval
## Retrieve all Status lists [#retrieve-all-status-lists]
## Retrieve a Status list [#retrieve-a-status-list]
## Retrieve a Status list token [#retrieve-a-status-list-token]
## Status list distribution [#status-list-distribution]
# Status List Signers
URL: /docs/issuance/revocation/api-reference/mdocs-status-list-signers
## Create a Status List Signers [#create-a-status-list-signers]
## Revoke a Status List Signer [#revoke-a-status-list-signer]
## Retrieve all Status List Signers [#retrieve-all-status-list-signers]
## Retrieve a Status List Signer [#retrieve-a-status-list-signer]
## Update a Status List Signer [#update-a-status-list-signer]
## Delete a Status List Signer [#delete-a-status-list-signer]
# Status update
URL: /docs/issuance/revocation/api-reference/mdocs-status-update
## Update mDocs status [#update-mdocs-status]
## Retrieve mDocs status [#retrieve-mdocs-status]
# Revocation lists
URL: /docs/issuance/revocation/api-reference/semantic-cwt-revocation-lists
## Retrieve all Semantic CWT credentials revocation lists [#retrieve-all-semantic-cwt-credentials-revocation-lists]
## Retrieve a Semantic CWT credential revocation list [#retrieve-a-semantic-cwt-credential-revocation-list]
# Status update
URL: /docs/issuance/revocation/api-reference/semantic-cwt-status-update
## Update Semantic CWT credential status [#update-semantic-cwt-credential-status]
## Retrieve Semantic CWT credential status [#retrieve-semantic-cwt-credential-status]
# Verification request signers
URL: /docs/verification/certificates/api-reference/verification-request-signers
## Create a Verification request signer [#create-a-verification-request-signer]
## Retrieve all Verification request signers [#retrieve-all-verification-request-signers]
## Retrieve a Verification request signer [#retrieve-a-verification-request-signer]
## Update a Verification request signer [#update-a-verification-request-signer]
## Delete a Verification request signer [#delete-a-verification-request-signer]
# Verifier root CA certificates
URL: /docs/verification/certificates/api-reference/verifier-root-ca-certificates
## Create a Verifier root CA certificate [#create-a-verifier-root-ca-certificate]
## Retrieve all Verifier root CA certificates [#retrieve-all-verifier-root-ca-certificates]
## Retrieve a Verifier root CA certificate [#retrieve-a-verifier-root-ca-certificate]
## Update a Verifier root CA certificate [#update-a-verifier-root-ca-certificate]
## Delete a Verifier root CA certificate [#delete-a-verifier-root-ca-certificate]
# Handling verification results
URL: /docs/verification/remote-mobile-verifiers/guides/handling-verification-results
Description: Learn how to interpret mDoc verification results returned by the mDocs Verifier SDK in remote mobile presentation flows.
When a holder responds to a remote mobile verification request, the mDocs Verifier SDK returns structured data containing the verification outcome. This guide explains the response structure and how to interpret it.
## What gets verified [#what-gets-verified]
Before working through the response structure, it is worth being clear about what is actually
being verified. The credential itself, the full mDoc as issued and stored in the holder's
wallet, never leaves the wallet and is never shared with a verifier. What the holder shares is
a *credential presentation*: a subset of the credential's data, accompanied by the issuer's
signature over the released items and a device authentication that proves the holder's device
authorized this specific presentation.
This model is grounded in [ISO/IEC 18013-7](https://www.iso.org/standard/91154.html), which
defines remote mDL and mDoc verification and specifies the wire protocols used to exchange a
presentation between holder and verifier: the OpenID for Verifiable Presentations (OID4VP)
protocol (Annex B), and the W3C Digital Credentials (DC) API integration (Annex C and Annex D).
Regardless of the wire protocol, what the holder shares is a presentation derived from the
credential rather than the credential itself, and the presentation carries the same
cryptographic proof and integrity guarantees as the credential it is drawn from.
When you verify a response, you are verifying the credential presentation. The cryptographic
trust checks (issuer signature, issuer trust, validity, revocation, device key binding) apply
to that presented slice, not to the credential as a whole on the holder's device. The MATTR VII
`verificationResult.verified` flag reflects whether this presented credential passed those
checks.
The rest of this guide uses "credential" as shorthand for the presented credential when the
context makes clear we are talking about what was returned in the response.
## How results are delivered [#how-results-are-delivered]
In a remote mobile presentation flow, the holder's wallet app responds to a verification request initiated by your verifier app on the same device. After the holder consents to sharing their credentials, the SDK returns an `OnlinePresentationSessionResult` object.
Your application receives the result inline from the SDK and is responsible for parsing and acting on it.
## Understanding the response [#understanding-the-response]
An `OnlinePresentationSessionResult` contains:
* **`challenge`** (optional): The unique challenge of the session, used to verify response authenticity.
* **`mobileCredentialResponse`** (optional): A `MobileCredentialResponse` containing the credential data returned by the holder.
* **`error`** (optional): An error object if the presentation workflow could not be completed.
When the holder responds to a verification request, the result will either contain a `mobileCredentialResponse` or an `error`.
From Android Verifier SDK v7.0.0, `OnlinePresentationSessionResult` is a sealed interface with
`Success` (carrying `mobileCredentialResponse`) and `Failure` (carrying `error`) variants — branch
over it with `when`/`is` rather than inspecting nullable fields. From iOS Verifier SDK v6.0.0, it is
a `@frozen` enum with `success(sessionId:challenge:mobileCredentialResponse:)` and
`failure(sessionId:challenge:error:)` cases — branch over it with `switch`/`case`. The serialized
structure documented on this page is the same across platforms.
### Session-level errors [#session-level-errors]
If the `error` field is present, the holder was unable to complete the presentation workflow. The error object contains:
* **`type`**: Indicates why the presentation failed
* **`message`**: A human-readable description of the error
The `type` values are:
* **`SessionAborted`**: The user explicitly aborted the session before it completed.
* **`ResponseError`**: An error occurred while processing the response from the holder's wallet.
* **`VerificationError`**: The submitted presentation response is invalid.
* **`WalletUnavailable`**: The wallet appears to be unavailable and unable to respond to the request.
* **`Unknown`**: An unknown error occurred while processing the presentation response.
**Example**: The user cancels the mDL presentation request:
```json title="Example session error when the user aborts the session"
{
"challenge": "c5a27e4c-85b6-4b3c-9f1a-2d8e5f3a4b7c",
"error": {
"type": "SessionAborted",
"message": "User aborted the session"
}
}
```
### Credential response [#credential-response]
When no session-level error occurs, the `mobileCredentialResponse` field contains the holder's response. A `MobileCredentialResponse` has two optional fields:
* **`credentials`**: An array of `MobileCredentialPresentation` objects representing credentials that were successfully returned.
* **`credentialErrors`**: An array of `CredentialError` objects representing credentials that couldn't be returned.
A successful session (no `error`) can still result in several credential-level outcomes, often
in combination. Each one is described along two independent axes: whether the requested
credential itself verified, and whether the requested claims within it were provided.
1. **Requested credential not presented**: The holder doesn't have the requested credential, so there is nothing to verify. The credential appears under `credentialErrors` with an error code of `notReturned`, and no `verificationResult` is produced.
2. **Presented credential not verified**: A credential was provided but the credential-level trust checks failed. The `credentials` array contains the credential with `verified: false` and a `reason.type` explaining why. Even if claims were returned, they should not be relied on while the credential itself did not verify.
3. **Presented credential verified, all claims provided**: The credential-level checks pass (`verified: true` with no `reason`) and every requested claim is returned (no `claimErrors`). Both layers are clean.
4. **Presented credential verified, some claims missing**: The credential-level checks pass (`verified: true`), but one or more requested claims were not returned and appear under `claimErrors`. The credential is trustworthy; the data payload is incomplete. How to handle the missing data is up to the relying party, depending on its use case. Options include failing the flow, falling back to collecting the required data through another channel, or proceeding if the missing claim is not essential.
## Detailed result structure [#detailed-result-structure]
### Credential-level information [#credential-level-information]
Each `MobileCredentialPresentation` in the `credentials` array contains:
* **`docType`**: The credential type (e.g., `org.iso.18013.5.1.mDL` for a mobile driver's license).
* **`claims`**: Verified claims organized by namespace.
* **`claimErrors`**: Errors for individual claims that couldn't be verified or were not returned.
* **`validityInfo`**: Credential validity period timestamps (`signed`, `validFrom`, `validUntil`, `expectedUpdate`).
* **`verificationResult`**: Verification status containing:
* **`verified`**: Boolean indicating if verification succeeded. This is a high-level result; individual claim errors may still exist.
* **`reason`** (optional): Object explaining verification failures when `verified` is `false`. Contains a `type` value from the following:
* `DeviceKeyInvalid`: Device key is not valid. This can occur if the credential was not properly bound to the device or if the device's secure element is compromised.
* `InvalidSignerCertificate`: Invalid signer certificate. This can occur if the credential's signing certificate is not valid or has been tampered with.
* `IssuerNotTrusted`: Credential was issued by a certificate that is not trusted by the verifier. This can occur if the issuer's certificate is not included in the verifier's trusted issuer list.
* `MobileCredentialExpired`: Credential expired. This can occur if the current date is after the credential's `validUntil` date.
* `MobileCredentialInvalid`: Credential is not valid. This can occur for various reasons such as failing signature verification, containing invalid data, or not conforming to expected formats.
* `MobileCredentialNotYetValid`: Credential not yet valid. This can occur if the current date is before the credential's `validFrom` date.
* `StatusRevoked`: Credential has been revoked. This can occur if the issuer has revoked the credential after it was issued, which is typically checked through a revocation mechanism provided by the issuer.
* `StatusUnknown`: Credential status could not be determined. This can occur if the verifier is unable to check the revocation status of the credential due to network issues or if the issuer does not provide a revocation mechanism.
* `TrustedIssuerCertificateExpired`: Trusted issuer certificate expired. This can occur if the certificate of the trusted issuer has passed its expiration date, which may affect the trustworthiness of credentials issued by that issuer.
* `TrustedIssuerCertificateNotYetValid`: Trusted issuer certificate not yet valid. This can occur if the certificate of the trusted issuer is not yet valid (i.e., the current date is before the certificate's `validFrom` date), which may affect the trustworthiness of credentials issued by that issuer.
* `UnsupportedCurve`: Credential object contains an unsupported curve. This can occur if the credential uses cryptographic curves that are not supported by the verifier's cryptographic library, which may prevent successful verification of the credential's signatures.
* **`issuerInfo`** (optional): Issuer details including `commonName` and `trustedIssuerId`.
* **`branding`** (optional): Visual information for displaying the credential (name, description, colors, logos). You can use this to create a rich user interface when showing credential details in your application.
The failure object serializes as the `reason` field. In the native iOS (v6.0.0+) and Android
(v7.0.0+) Verifier SDKs, it is accessed in code via the `failureType` property (it still serializes
as `reason`). In the React Native SDK it is accessed as `reason`.
### Claim-level information [#claim-level-information]
Claims are organized by namespace within the `claims` object. For example, for an mDL, claims appear under the `org.iso.18013.5.1` namespace:
```json title="Example claims structure for a verified mDL credential"
"claims": {
"org.iso.18013.5.1": {
"family_name": { "value": "Smith" },
"given_name": { "value": "Jane" },
"birth_date": { "value": "1990-05-15" },
"address": { "value": "123 Main Street, Springfield" }
}
}
```
If specific claims couldn't be verified or weren't provided, they appear in the `claimErrors` object with the same namespace structure and an error code of `notReturned`:
```json title="Example claim errors for a credential with a missing claim"
"claimErrors": {
"org.iso.18013.5.1": {
"portrait": "notReturned"
}
}
```
### Credential errors [#credential-errors]
When a requested credential wasn't provided by the holder, it appears in the `credentialErrors` array with a `docType` and an `errorCode` of `notReturned`:
```json title="Example credential errors for a missing credential"
"credentialErrors": [
{
"docType": "org.iso.18013.5.1.mDL",
"errorCode": "notReturned"
}
]
```
## Understanding the `verified` flag [#understanding-the-verified-flag]
With the structure in mind, it is worth being explicit about what
`verificationResult.verified` does and does not tell you, because it is the single most important
field for deciding whether to trust a presentation.
`verified` is the pass/fail flag for the presented credential. It applies to the slice of
credential data the holder released in this session, together with its associated issuer
signature and device authentication, not to the credential as a whole as stored on the holder's
device. When `verified: true`, what the holder presented has passed MATTR VII's cryptographic
and trust checks: the issuer signature on the released items is intact, the credential was
issued by a trusted issuer, it is not expired or revoked, and the device key binding holds. In
plain terms, the presented mDoc is genuine, has not been tampered with, and is currently valid.
What `verified: true` does not mean is that every claim you requested came back. `verified` is a
high-level result on the credential as a whole, and individual claim errors may still exist. A
credential can return `verified: true` while a specific requested claim (for example, `portrait`)
is missing and surfaces under `claimErrors` with an error code of `notReturned`. The credential
is trustworthy; the data payload may still be incomplete.
The mirror image is `verified: false`. When the credential layer fails, MATTR VII surfaces a
`reason.type` explaining why (for example, expired, revoked, untrusted issuer, invalid signer
certificate, or broken device key binding). The `reason` field is typed as optional, so
defensive code should handle the case where it is absent. See the
[credential-level information](#credential-level-information) section above for the full list of
`reason.type` values.
### Two layers of checks [#two-layers-of-checks]
A relying party's business logic needs to check two distinct things:
1. **Is the credential real and valid?** Look at `verificationResult.verified` on each credential
in the `credentials` array. This is the objective trust check, and it should not be overridden
by business logic.
2. **Did we get all the data we asked for?** Look at `claimErrors` on each credential, and at
`credentialErrors` on the credential response. Whether a missing claim or credential is
acceptable is a business-logic decision specific to your use case.
In other words, `verified` is the objective measure of what can or cannot be accepted at all.
Everything else, including `claimErrors`, `credentialErrors`, and the specific claim values
returned, feeds your business logic about whether to accept the presentation for your particular
use case.
Treating `verified: true` as a green light to proceed without inspecting `claimErrors` is a
common integration mistake. The credential may be cryptographically valid while still missing a
field your use case requires. For example, an age-restricted purchase flow needs `birth_date`,
but if only `family_name` came back the relying party cannot make an age decision even though
the credential itself returned `verified: true`. Conversely, bypassing a `verified: false`
result with business logic (for example, accepting an expired or revoked credential because the
claims look correct) defeats the trust model and should not be done.
`verified` is MATTR VII's API shape. The underlying trust evaluation for mDL and mDoc
credentials follows ISO/IEC 18013-5, but the `verified` boolean itself is not a standards-defined
field.
## Complete examples [#complete-examples]
### Session error [#session-error]
The wallet couldn't be reached:
```json title="Example response for a wallet unavailable error"
{
"challenge": "c5a27e4c-85b6-4b3c-9f1a-2d8e5f3a4b7c",
"error": {
"type": "WalletUnavailable",
"message": "The wallet is unavailable"
}
}
```
### Requested credential not presented [#requested-credential-not-presented]
Neither layer of the check produces a result here. The holder did not provide a matching mDL, so
the credential layer has nothing to verify (no `verificationResult` is returned) and the claims
layer is not applicable. The missing credential is reported under `credentialErrors`, and the
relying party's business logic must decide how to handle it.
```json title="Example response for a requested credential that was not presented"
{
"challenge": "c5a27e4c-85b6-4b3c-9f1a-2d8e5f3a4b7c",
"mobileCredentialResponse": {
"credentialErrors": [
{
"docType": "org.iso.18013.5.1.mDL",
"errorCode": "notReturned"
}
]
}
}
```
### Presented credential not verified [#presented-credential-not-verified]
The credential layer fails. The holder provided an mDL but the credential-level trust checks
failed (in this example, because the credential has expired): `verified: false`, with
`reason.type` identifying why. Even if claims were returned, they should not be relied on while
the credential itself did not verify. The credential should not be accepted regardless of
business logic.
```json title="Example response for a presented credential that did not verify (expired mDL)"
{
"challenge": "c5a27e4c-85b6-4b3c-9f1a-2d8e5f3a4b7c",
"mobileCredentialResponse": {
"credentials": [
{
"docType": "org.iso.18013.5.1.mDL",
"verificationResult": {
"verified": false,
"reason": {
"type": "MobileCredentialExpired",
"message": "Credential has expired"
}
},
"validityInfo": {
"signed": "2024-01-15T10:00:00Z",
"validFrom": "2024-01-15T10:00:00Z",
"validUntil": "2025-01-15T10:00:00Z",
"expectedUpdate": "2026-01-15T10:00:00Z"
}
}
]
}
}
```
### Presented credential verified, all claims provided [#presented-credential-verified-all-claims-provided]
Both layers pass. The credential layer returns `verified: true` with no `reason`, confirming the
mDL is genuine, current, and from a trusted issuer. The claims layer returns every requested
claim with no `claimErrors`, so the relying party has the complete data payload it asked for.
```json title="Example response for a presented credential that verified with all requested claims returned"
{
"challenge": "c5a27e4c-85b6-4b3c-9f1a-2d8e5f3a4b7c",
"mobileCredentialResponse": {
"credentials": [
{
"docType": "org.iso.18013.5.1.mDL",
"claims": {
"org.iso.18013.5.1": {
"family_name": { "value": "Smith" },
"given_name": { "value": "Jane" },
"birth_date": { "value": "1990-05-15" },
"address": { "value": "123 Main Street, Springfield" }
}
},
"validityInfo": {
"signed": "2024-01-15T10:00:00Z",
"validFrom": "2024-01-15T10:00:00Z",
"validUntil": "2027-01-15T10:00:00Z",
"expectedUpdate": "2028-01-15T10:00:00Z"
},
"verificationResult": {
"verified": true
},
"issuerInfo": {
"commonName": "State Department of Motor Vehicles",
"trustedIssuerId": "d4a6e9f2-3b1c-4d8e-a5f7-9c2b0e8d1a3f"
},
"branding": {
"name": "Driver License",
"description": "State-issued driver license",
"backgroundColor": "#1E3A8A",
"issuerLogo": {
"format": "svg",
"data": "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg=="
}
}
}
]
}
}
```
### Presented credential verified, some claims missing [#presented-credential-verified-some-claims-missing]
The credential layer passes but the claims layer is incomplete. The mDL itself returns
`verified: true` and is trustworthy, while one requested claim (`portrait`) was not released and
appears under `claimErrors` as `notReturned`. What is missing here is data, not trust, and the
relying party's business logic must decide whether the missing claim is acceptable for its use
case.
```json title="Example response for a presented credential that verified with a missing claim"
{
"challenge": "c5a27e4c-85b6-4b3c-9f1a-2d8e5f3a4b7c",
"mobileCredentialResponse": {
"credentials": [
{
"docType": "org.iso.18013.5.1.mDL",
"claims": {
"org.iso.18013.5.1": {
"family_name": { "value": "Smith" },
"given_name": { "value": "Jane" },
"birth_date": { "value": "1990-05-15" }
}
},
"claimErrors": {
"org.iso.18013.5.1": {
"portrait": "notReturned"
}
},
"validityInfo": {
"signed": "2024-01-15T10:00:00Z",
"validFrom": "2024-01-15T10:00:00Z",
"validUntil": "2027-01-15T10:00:00Z",
"expectedUpdate": "2028-01-15T10:00:00Z"
},
"verificationResult": {
"verified": true
},
"issuerInfo": {
"commonName": "State Department of Motor Vehicles",
"trustedIssuerId": "d4a6e9f2-3b1c-4d8e-a5f7-9c2b0e8d1a3f"
}
}
]
}
}
```
## Recommended handling flow [#recommended-handling-flow]
When processing an `OnlinePresentationSessionResult`, follow these steps:
1. **Check for session-level errors**: If the `error` field is present, the presentation workflow failed before any credential data was returned. Handle based on the `error.type` value.
2. **Validate the challenge**: Compare the returned `challenge` against the one you generated when creating the session. This protects against session replay attacks.
3. **Check for credential errors**: Inspect `mobileCredentialResponse.credentialErrors` to determine if any requested credentials were not returned. Handle these based on whether the credential is required for your use case.
4. **Iterate through credentials**: For each `MobileCredentialPresentation` in `mobileCredentialResponse.credentials`:
* Check `verificationResult.verified`. If `false`, inspect `verificationResult.reason` to understand why.
* If `verified` is `true`, proceed to extract claim values from the `claims` object.
* Check `claimErrors` for any claims that were requested but not returned. Decide whether to proceed based on which claims are missing.
5. **Apply business logic**: Use the verified claims, issuer information, and branding data to make authorization decisions and present results in your application.
Challenge validation protects against session replay attacks. Generate a unique challenge when creating the session, and verify it matches the one returned in the result.
## Next steps [#next-steps]
* Review the mDocs Verifier SDK API reference for [iOS](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/) or [Android](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/) for complete type definitions
* Explore the [remote mobile verification workflow](/docs/verification/remote-mobile-verifiers/workflow) for the complete end-to-end process
* Follow the [quickstart](/docs/verification/remote-mobile-verifiers/quickstart) or [tutorial](/docs/verification/remote-mobile-verifiers/tutorial) for practical implementation examples
# Build a mobile application that can verify an mDoc from an Apple Wallet installed on the same device
URL: /docs/verification/remote-mobile-verifiers/guides/verify-with-apple-wallet
## Overview [#overview]
This guide demonstrates how to use the
[mDocs Verifier Mobile SDK](/docs/verification/remote-mobile-verifiers/sdks/overview) to build a mobile
application that can verify an [mDoc](/docs/concepts/mdocs) presented from an Apple Wallet, using the
[Verify with Wallet API](https://developer.apple.com/wallet/get-started-with-verify-with-wallet/).
The guide covers both the **iOS** and **React Native** mDocs Verifier SDKs. Verify with Apple Wallet
is an Apple-only API, so there is no Android coverage. Even when you build your verifier app with
React Native, the Verify with Apple Wallet flow only runs on iOS. Use the tabs throughout the guide
to follow the steps for your chosen SDK.
## Prerequisites [#prerequisites]
Before you get started, let's make sure you have everything you need.
* You will need access to a [MATTR VII tenant](/docs/resources/get-started) and
know how to [make API requests](/docs/platform-management/portal#interacting-with-the-tenant).
### MATTR Resources [#mattr-resources]
As part of your MATTR Pi SDK onboarding process you should have been provided with access to the SDK
resources for your chosen platform:
* ZIP file which includes the required framework:
(`MobileCredentialVerifierSDK-*version*.xcframework.zip`).
* Sample Verifier app: You can use this app for reference as you work through this guide.
This guide is only meant to be used with the most [recent
version](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/changelog)
of the iOS mDocs Verifier SDK.
* The
[@mattrglobal/mobile-credential-verifier-react-native](https://www.npmjs.com/package/@mattrglobal/mobile-credential-verifier-react-native)
npm package, provided as part of your MATTR Pi SDK onboarding process.
Verify with Apple Wallet support was added to the React Native mDocs Verifier SDK in **v9.0.0**
(functional from **v9.0.1**) and requires **iOS 16 or above**. Although the app is built with React
Native, the Verify with Apple Wallet flow only runs on iOS. Calling it on Android returns a
`PlatformNotSupported` error. This guide is only meant to be used with the most [recent
version](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/index.html)
of the React Native mDocs Verifier SDK.
### Apple resources [#apple-resources]
* Submit an [entitlement request](https://developer.apple.com/contact/request/verify-with-wallet/)
to use the Verify with Wallet API. This request would include your app information as well as what
information you would like to request for verification and why. You can only proceed with this
guide after your request has been approved by Apple.
* For testing the end-to-end flow, you will need to install the
[Wallet Identity Developer profile](https://developer.apple.com/bug-reporting/profiles-and-logs/?name=wallet\&platform=ios).
Got everything? Let's get going!
## Workflow [#workflow]
The following diagram depicts the workflow you will build in this guide:
1. The user performs an action in a mobile app (**Verifier Application**) that requires a
credential to be presented for verification.
2. The **Verifier Application** interacts with the **Verify with Wallet API** to validate the
request can be met using the **Apple Wallet**. This includes validating:
* The identity of the verifier.
* That the verifier is entitled to verify the requested credential type.
* That the user has a matching credential in their **Apple Wallet**.
3. The **Verify with Wallet API** returns the results of the entitlement validation to the
**Verifier Application**.
4. If (and only if) all entitlements are valid, the **Verifier Application** presents a *Verify with
Apple Wallet* button to the **user**.
5. The **user** taps the *Verify with Apple Wallet* button.
6. The **Verifier Application** starts a Verify with Apple Wallet presentation session with the
configured **MATTR VII tenant**.
7. The **MATTR VII tenant** calls the **Verify with Wallet API** with a credentials request.
8. The **Verify with Wallet API** invokes the user's **Apple Wallet** to request the credential.
9. The **Apple Wallet** displays a popup interface to the **user** requesting their consent to share
the requested information (they never leave the **Verifier Application**).
10. The **user** provides their consent to sharing a specific credential from the **Apple Wallet**.
11. The user's **Apple Wallet** sends an Apple encrypted credential to the **MATTR VII tenant**.
12. The **MATTR VII tenant** decrypts and verifies the credential.
13. The **MATTR VII tenant** sends the verification results to the **Verifier Application**.
14. The **Verifier Application** displays the verification results and the **user** continues the
interaction accordingly.
You will build this workflow in three parts:
1. [Part 1: Setup the MATTR VII Verifier tenant](#part-1-setup-the-mattr-vii-verifier-tenant).
2. [Part 2: Configure your Apple Developer account](#part-2-configure-your-apple-developer-account).
3. [Part 3: Build a mobile application](#part-3-build-a-mobile-application).
## Part 1: Setup the MATTR VII Verifier tenant [#part-1-setup-the-mattr-vii-verifier-tenant]
The MATTR VII tenant will be used to interact with your mobile application (generating a
verification request) and the wallet application (presenting an mDoc for verification) as per OID4VP
and ISO/IEC 18013-7 Annex B. To enable this, you must:
1. [Create a verifier application configuration](#create-a-verifier-application-configuration):
Define what applications can create verification sessions with the MATTR VII tenant, and how to
handle these requests.
2. [Create a trusted wallet provider configuration](#create-a-trusted-wallet-provider-configuration):
Define how to invoke specific wallet applications as part of a remote verification workflow.
3. [Configure a trusted issuer](#configure-a-trusted-issuer): The MATTR VII verifier tenant will
only accept mDocs issued by these trusted issuers.
### Create a verifier application configuration [#create-a-verifier-application-configuration]
Each MATTR VII tenant can interact with multiple verifier applications, and can handle requests
differently for each application. This means you must create a verifier application configuration
that defines how to handle verification requests from your mobile application.
The verifier application *Type* is always `iOS` for the Verify with Apple Wallet flow, regardless of
the SDK you build with. The credential is always presented through iOS/Apple Wallet, even when your
verifier app is built with React Native.
1. In the navigation panel on the left-hand side, expand the **Credential Verification** menu.
2. Select **Applications**.
3. Select **Create new**.
4. Enter a meaningful *Name* for your application (e.g. "Verify with Wallet Application").
5. Use the *Type* dropdown to select `iOS` as you are building an iOS application.
6. Enter your Apple Developer Team ID in the *Team ID* field. You can find it in the
*Membership details* section of your
[Apple Developer account](https://developer.apple.com/account).
7. Enter your iOS application bundle identifier in the *Bundle ID* field. This is the unique
identifier of your iOS application, which you can set in your
[Xcode project settings](https://developer.apple.com/documentation/xcode/preparing-your-app-for-distribution/#Set-the-bundle-ID).
This will be used by the MATTR VII tenant to validate incoming requests are from a known and
trusted application.
8. Select **Create**.
Make the following request to your MATTR VII tenant to
[create a verifier application configuration](/docs/verification/remote-verification-api-reference/verifier-applications#create-a-verifier-application):
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "Verify with Wallet Application",
"type": "ios",
"teamId": "A2B3C4D5E6",
"bundleId": "io.mattrlabs.dev.sampleApp.MdocSampleApp",
"resultAvailableInFrontChannel": true
}
```
* `name` : You can use whatever name you'd like, as long as it is unique on your tenant.
* `type` : Use `ios` as you are building an iOS verifier application.
* `teamId` : Replace with your Apple Developer Team ID associated with your iOS application. You can
find it in the *Membership details* section of your
[Apple Developer account](https://developer.apple.com/account).
* `bundleId` : Replace with your iOS application bundle identifier. This is the unique identifier of
your iOS application, which you can set in your
[Xcode project settings](https://developer.apple.com/documentation/xcode/preparing-your-app-for-distribution/#Set-the-bundle-ID).
This must match the bundle ID you included in your Verify with Apple Wallet entitlement request.
* `resultAvailableInFrontChannel` : Setting this to `true` makes the verification results available
directly to the verifier application.
*Response*
```json title="Response body"
{
"id": "0eaa8074-8cc4-41ec-9e42-072d36e2acb0"
//... rest of application configuration
}
```
* `id` : You will use this value later to initialize the SDK so that requests coming from your
verifier application can be recognized and trusted by the MATTR VII tenant.
### Configure a trusted issuer [#configure-a-trusted-issuer]
You must configure trusted issuers on your MATTR VII verifier tenant, as presented mDocs will only
be verified if they had been issued by a trusted issuer. This is achieved by providing the PEM
certificate of the IACA used by these issuers to sign mDocs.
1. In the navigation panel on the left-hand side, expand the **Credential Verification** menu.
2. Click on **Trusted issuers**.
3. Click on **Create new**.
4. In the *Certificate PEM file* field, paste the PEM certificate of the IACA used by the issuers you want to
trust. For testing purposes, you can use the IACA of the
[Apple Developer Integrator profile](https://developer.apple.com/bug-reporting/profiles-and-logs/?name=wallet)
referenced in the [prerequisites](#prerequisites) section.
5. Click on **Add**.
Make the following request to your MATTR VII tenant to [configure a trusted issuer](/docs/verification/remote-verification-api-reference/trusted-issuers#create-a-trusted-issuer):
```http title="Request"
POST /v2/credentials/mobile/trusted-issuers
```
```json title="Request body"
{
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIICYzCCAgmgAwIBAgIKXhjLoCkLWBxREDAKBggqhkjOPQQDAjA4MQswCQYDVQQG\nEwJBVTEpMCcGA1UEAwwgbW9udGNsaWZmLWRtdi5tYXR0cmxhYnMuY29tIElBQ0Ew\nHhcNMjQwMTE4MjMxNDE4WhcNMzQwMTE1MjMxNDE4WjA4MQswCQYDVQQGEwJBVTEp\nMCcGA1UEAwwgbW9udGNsaWZmLWRtdi5tYXR0cmxhYnMuY29tIElBQ0EwWTATBgcq\nhkjOPQIBBggqhkjOPQMBBwNCAASBnqobOh8baMW7mpSZaQMawj6wgM5e5nPd6HXp\ndB8eUVPlCMKribQ7XiiLU96rib/yQLH2k1CUeZmEjxoEi42xo4H6MIH3MBIGA1Ud\nEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRFZwEOI9yq\n232NG+OzNQzFKa/LxDAuBgNVHRIEJzAlhiNodHRwczovL21vbnRjbGlmZi1kbXYu\nbWF0dHJsYWJzLmNvbTCBgQYDVR0fBHoweDB2oHSgcoZwaHR0cHM6Ly9tb250Y2xp\nZmYtZG12LnZpaS5hdTAxLm1hdHRyLmdsb2JhbC92Mi9jcmVkZW50aWFscy9tb2Jp\nbGUvaWFjYXMvMjk0YmExYmMtOTFhMS00MjJmLThhMTctY2IwODU0NWY0ODYwL2Ny\nbDAKBggqhkjOPQQDAgNIADBFAiAlZYQP95lGzVJfCykhcpCzpQ2LWE/AbjTGkcGI\nSNsu7gIhAJfP54a2hXz4YiQN4qJERlORjyL1Ru9M0/dtQppohFm6\n-----END CERTIFICATE-----"
}
```
* `certificatePem` : Replace with the PEM certificate of the IACA used by the issuers you want to
trust. For testing purposes, you can use the IACA of the
[Apple Developer Integrator profile](https://developer.apple.com/bug-reporting/profiles-and-logs/?name=wallet)
you installed as part of the prerequisites.
*Response*
A successful `201` response indicates that this issuer's certificate was added to your MATTR VII
tenant's trusted issuer's list. This means that mDocs that use this IACA as their root certificate
can be trusted and verified.
### Create an Apple Identity Access certificate [#create-an-apple-identity-access-certificate]
To enable your MATTR VII tenant to interact with Apple Wallet as part of a remote verification
workflow, you must create an Apple Identity Access certificate. This certificate is used to sign
requests sent to the Verify with Wallet API.
Currently this action is not available in the MATTR Portal and can only be performed via an API request.
Make the following request to your MATTR VII tenant to
[create an Apple Identity Access certificate](/docs/verification/remote-verification-api-reference/apple-identity-access-csr#create-an-apple-identity-access-certificate-signing-request):
```http title="Request"
POST /v2/presentations/certificates/apple-identity-access-certificates
```
```json title="Request body"
{
"teamId": "A2B3C4D5E6",
"merchantId": "com.domain.subdomain"
}
```
* `teamId` : Replace with your Apple Developer Team ID associated with your iOS application. You can
find it in the *Membership details* section of your
[Apple Developer account](https://developer.apple.com/account).
* `merchantId` : Replace with the merchant ID you created as part of your
[Verify with Apple Wallet entitlement request](https://developer.apple.com/contact/request/verify-with-wallet/).
*Response*
A successful `201` response indicates that the Apple Identity Access certificate was created
successfully:
```json title="Response body"
{
"id": "fd44e792-45ac-11f0-bef8-bb24f133065e",
"teamId": "A2B3C4D5E6",
"merchantId": "com.domain.subdomain",
"csrPem": "string" // [!code focus]
}
```
Make note of the `csrPem` value, as you will need it in the next part to create a matching Apple
Identity Access certificate in your Apple Developer account.
## Part 2: Configure your Apple Developer account [#part-2-configure-your-apple-developer-account]
Now that you have created the Apple Identity Access certificate in your MATTR VII tenant, you must
create a matching certificate in your Apple Developer account and configure it accordingly.
### Create an Identity Access Certificate [#create-an-identity-access-certificate]
Log into your [Apple Developer account](https://developer.apple.com/account) and create a new Apple
Pay Identity Access certificate using the CSR you obtained in the previous step. For detailed
instructions, see
[Create an Apple Pay Identity Access certificate](https://developer.apple.com/help/account/capabilities/configure-apple-pay/).
Once this certificate is created, the Verify With Wallet API will be able to validate requests
coming from your MATTR VII tenant, as they are signed using the private key associated with this
certificate.
## Part 3: Build a mobile application [#part-3-build-a-mobile-application]
Now that the MATTR VII verifier tenant and your Apple developer account are both properly
configured, you can proceed with the steps required to embed verification capabilities into your
mobile verifier application. Use the tabs in each step to follow the instructions for your chosen
SDK:
### Environment setup [#environment-setup]
#### Create a new project [#create-a-new-project]
Follow the detailed instructions to
[Create a new Xcode Project](https://help.apple.com/xcode/mac/current/#/dev07db0e578) and add your
organization's identifier.
#### Unzip the dependencies file [#unzip-the-dependencies-file]
1. Unzip the [`MobileCredentialVerifierSDK-*version*.xcframework.zip` file](#mattr-resources).
2. Drag the `MobileCredentialVerifierSDK-*version*.xcframework` folder into your project.
3. Configure `MobileCredentialVerifierSDK.xcframework` to
[Embed & sign](https://help.apple.com/xcode/mac/current/#/dev51a648b07).
See [Add existing files and folders](https://help.apple.com/xcode/mac/current/#/dev81ce1d383) for
detailed instructions.
This should result in the following framework being added to your project:
#### Run the application [#run-the-application]
Select **Run** and make sure the application launches with a *“Hello, world!”* text in the middle of
the display, as shown in the following image:
#### Install the SDK [#install-the-sdk]
Add the React Native mDocs Verifier SDK to your project:
```bash title="Install the SDK"
yarn add @mattrglobal/mobile-credential-verifier-react-native
```
#### Generate the native iOS project [#generate-the-native-ios-project]
This guide uses [Expo](https://docs.expo.dev/) with development builds. Generate the native `ios/`
project so you can add the required capabilities in Xcode:
```bash title="Generate the native project"
yarn expo prebuild
```
#### Run the application [#run-the-application-1]
Connect a **physical iOS device** and run the application on it:
```bash title="Run iOS application"
yarn ios --device
```
Verify with Apple Wallet cannot be tested on an iOS simulator. You must run the application on a
physical iOS device (iOS 16 or above) to complete the flow with a wallet installed on the same
device.
### Configure application entitlement files [#configure-application-entitlement-files]
To enable your application to interact with the Apple Wallet, you must add the following capabilities
to your Xcode project:
1. Add the *In App Identity Presentment* capability and define the document types and elements you
want to request for verification. You can find a list of supported document types and elements in
the
[Apple Developer documentation](https://developer.apple.com/documentation/passkit/configuring-your-environment-for-the-verify-with-wallet-api#Configure-your-apps-entitlement-file).
2. Add the *In App Identity Presentment Merchant IDs* and define the merchant IDs you created as
part of your
[Verify with Apple Wallet entitlement request](https://developer.apple.com/contact/request/verify-with-wallet/).
You can add these via the *Signing & Capabilities* tab of your Xcode project.
For detailed instructions, see [Add capabilities to your
app](https://developer.apple.com/documentation/xcode/adding-capabilities-to-your-app).
**Identity Presentment capabilities**
**Identity Presentment capabilities configuration**
Once `yarn expo prebuild` has generated the `ios/` project, open it in Xcode and add the same
capabilities described above via the *Signing & Capabilities* tab, exactly as in the iOS steps. For
detailed instructions, see [Add capabilities to your
app](https://developer.apple.com/documentation/xcode/adding-capabilities-to-your-app).
**Identity Presentment capabilities**
**Identity Presentment capabilities configuration**
Re-running `yarn expo prebuild --clean` regenerates the native `ios/` project and may discard
manual Xcode changes. Re-apply these capabilities if you regenerate the project.
### Initialize the SDK [#initialize-the-sdk]
The first capability you will build into your app is to initialize the SDK so that the app can use
its functions and classes.
From v6.0.0, `initialize` is asynchronous and requires a `PlatformConfiguration` with both your tenant host and Verifier Application `id` (this drives [SDK Tethering](/docs/verification/sdks/sdk-tethering)):
```swift title="Initialize the SDK"
let platformConfiguration = PlatformConfiguration(
tenantHost: URL(string: "https://learn.vii.au01.mattr.global")!, // [!code highlight]
applicationId: ""
)
try await mobileCredentialVerifier.initialize(platformConfiguration: platformConfiguration)
```
* `tenantHost` : Replace with the URL of your MATTR VII tenant.
* `applicationId` : The `id` returned when you [created the verifier application](#create-a-verifier-application-configuration).
Import the SDK and call `initialize` with a `platformConfiguration` that sets your tenant host. The
function returns a [`neverthrow`](https://github.com/supermacro/neverthrow) `Result`, so errors are
surfaced via `result.isErr()` rather than a thrown exception:
```tsx title="Initialize the SDK"
import { initialize } from "@mattrglobal/mobile-credential-verifier-react-native";
const result = await initialize({
platformConfiguration: { tenantHost: "https://learn.vii.au01.mattr.global" }, // [!code highlight]
});
if (result.isErr()) {
// handle initialization error
}
```
* `tenantHost` : Replace with the URL of your MATTR VII tenant.
In the React Native SDK, the Verifier Application `id` is provided later, when you call
`fetchAppleWalletConfiguration`, rather than at initialization.
### Validate verification feasibility [#validate-verification-feasibility]
Before displaying a Verify with Apple Wallet button, the SDK must confirm that the request can be
met using the Apple Wallet. This includes validating:
* The identity of the verifier (using the provided `merchantId`).
* That the verifier is entitled to verify the requested credential type (using the provided `docType` in the credential request and comparing it against the entitlements associated with this `merchantId`).
* That the user has a credential that matches the requested `docType` in their **Apple Wallet**.
This is achieved by calling the `fetchAppleWalletConfiguration` method and providing a
`MobileCredentialRequest` (and a `merchantId`). The request defines what information is requested for
verification and is used by the Verify with Wallet API to determine whether the requested credential
type is supported by the entitlements associated with your application. The `merchantId` is the
merchant ID you created as part of your
[Verify with Apple Wallet entitlement request](https://developer.apple.com/contact/request/verify-with-wallet/),
and is used to determine whether this specific merchant is authorized to request the specified
credential type.
The `MobileCredentialRequest` defines what information is required for verification:
* The requested credential type (e.g. `org.iso.18013.5.1.mDL`).
* The claims required for verification (e.g. `family_name`).
* The requested namespace (e.g. `org.iso.18013.5.1`).
* Whether or not the verifier intends to persist the claim value (`true`/`false`).
Call the [`fetchAppleWalletConfiguration`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/mobilecredentialverifier/fetchapplewalletconfiguration\(request:merchantid:\)) method:
```swift title="Fetch Apple Wallet configuration"
func fetchAppleWalletConfiguration(
request: MobileCredentialRequest,
merchantId: String
) async -> Result
```
If all checks pass, this method returns an instance of the
[AppleWallet](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/applewallet)
class, which includes the UI elements and methods required to request a credential from an Apple
Wallet.
Your application can then present one of the returned UI elements (for example a *Verify with Apple
Wallet* button) to the user, which when tapped would invoke the Apple Wallet to request the
credential for verification.
**MobileCredentialRequest example**
```swift title="Swift"
let mobileCredentialRequest = MobileCredentialRequest(
docType: "org.iso.18013.5.1.mDL",
namespaces: [
"org.iso.18013.5.1": [
"family_name": false,
"given_name": false,
"birth_date": false
]
]
)
```
Call `fetchAppleWalletConfiguration` with the `request`, your verifier `applicationId`, and the
`merchantId`. It returns a `Result`:
```tsx title="Fetch Apple Wallet configuration"
import { fetchAppleWalletConfiguration } from "@mattrglobal/mobile-credential-verifier-react-native";
const result = await fetchAppleWalletConfiguration({
request: mobileCredentialRequest,
applicationId: "",
merchantId: "merchant.com.example.app",
});
if (result.isErr()) {
// request cannot be met via Apple Wallet, do not show the button
return;
}
const appleWallet = result.value;
```
* `applicationId` : The `id` returned when you
[created the verifier application](#create-a-verifier-application-configuration).
Unlike the iOS SDK, the React Native `AppleWallet` type exposes **only** a `requestMobileCredentials`
method. It does **not** return an SDK-rendered button or any UI elements. Your app renders its own
*Verify with Apple Wallet* button, enabling it only after `fetchAppleWalletConfiguration` succeeds.
**MobileCredentialRequest example**
```tsx title="React Native"
const mobileCredentialRequest = {
docType: "org.iso.18013.5.1.mDL",
namespaces: {
"org.iso.18013.5.1": {
family_name: false,
given_name: false,
birth_date: false,
},
},
};
```
### Request a credential from an Apple Wallet [#request-a-credential-from-an-apple-wallet]
Once the user taps the *Verify with Apple Wallet* button, you can call the `requestMobileCredentials`
method on the `AppleWallet` instance to invoke the Apple Wallet and request the mDoc for
verification.
* `challenge` : Unique value that must be generated by the verifier app. Should be a unique,
unpredictable value generated for each verification session to mitigate replay attacks by ensuring
the response from the Apple Wallet is tied to the current request and cannot be reused
maliciously. Always generate a new challenge for every credential request.
Once called, this method orchestrates the interaction to request the mDoc for verification from the
Apple Wallet. The user sees the Apple Wallet popup interface, where they can select the credential to
present for verification and provide their consent to share the requested information.
Call the [`requestMobileCredentials`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/applewallet/requestmobilecredentials\(challenge:\)) method:
```swift title="Request a credential from Apple Wallet"
func requestMobileCredentials(challenge: String) async throws -> OnlinePresentationSessionResult
```
Call `requestMobileCredentials` on the `appleWallet` instance returned by
`fetchAppleWalletConfiguration`. It returns a
`Result`:
```tsx title="Request a credential from Apple Wallet"
const result = await appleWallet.requestMobileCredentials(challenge);
if (result.isErr()) {
// handle error
return;
}
const sessionResult = result.value;
```
### Display verification results and continue the interaction [#display-verification-results-and-continue-the-interaction]
After the user consents, Apple Wallet sends the encrypted credential to the MATTR VII tenant. The
tenant decrypts and verifies it, then returns the verification results to your app as an
`OnlinePresentationSessionResult`.
The results are returned as an
[`OnlinePresentationSessionResult`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/onlinepresentationsessionresult).
Your app should then:
* Validate the challenge in the `OnlinePresentationSessionResult` matches the one you sent in the
request.
* Parse the `OnlinePresentationSessionResult` object to extract the verification results.
* Display the results to the user and continue the interaction accordingly.
After checking `result.isErr()` on the value returned by `requestMobileCredentials`, read the
`OnlinePresentationSessionResult` from `result.value` (the `sessionResult` in the previous step). Your
app should then:
* Validate the challenge in the `sessionResult` matches the one you sent in the request.
* Parse the `sessionResult` object to extract the verification results.
* Display the results to the user and continue the interaction accordingly.
# Android Verifier SDK v6.0.0 Migration Guide
URL: /docs/verification/remote-mobile-verifiers/sdks/android-6.0.0-migration-guide
## Overview [#overview]
This guide provides a comprehensive overview of the changes introduced in MobileCredentialVerifierSDK v6.0.0 for Android, including breaking changes, new features, and migration steps.
## Key Features [#key-features]
* **Improved reliability in contactless flows**: Enhanced BLE performance delivers more consistent proximity credential exchanges and faster engagements.
* **Stronger cryptography and standards alignment**: Updated COSE algorithms (as per RFC 9864) strengthen cryptographic compatibility and ensure continued compliance with evolving standards.
* **Simpler, Decoupled Releases**: The Android Verifier SDK no longer has a shared common module. This allows us to decouple the releases of Holder and Verifier and versions no longer need to match.
* **General stability and performance improvements**: Multiple refinements reduce integration friction, increase consistency across mobile environments, and improve overall user experience.
For a detailed list of changes included in this release, refer to the [SDK Changelog](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/).
## Breaking Changes [#breaking-changes]
This section outlines the breaking changes introduced in v6.0.0 that require updates to your existing implementation:
| # | Element | Change | Impact |
| - | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------- |
| 1 | `MobileCredentialVerifier.sendProximityPresentationRequest(skipStatusCheck = ...)` | Parameter renamed to `checkStatus = ...` with **inverted semantics** | All call sites using `skipStatusCheck = ...` must be updated. |
| 2 | `mattr.global.mobilecredentials.common` | Package path moved to `mattr.global.mobilecredentials.verifier` | All imports must be updated. |
## Bug Fixes [#bug-fixes]
* Fixed parsing of status lists that use a bit size other than 2.
* Fixed an issue where the Wallet could attempt to start a new session before the previous session had completed.
* Resolved BLE retry issues during proximity presentations that resulted in "SDK not ready (Engaging)" errors.
* Improved cross-device flow handling in DCM scenarios and resolved result mismatches.
* Corrected UI refresh issues following automatic session termination.
## Minimum Requirements [#minimum-requirements]
* Android 7 / Nougat / API 24.
* The Android Verifier SDK is built using Kotlin 2.0. This adds some intrinsic dependencies into your build tools.
* Kotlin 2.0 is supported from AGP version [8.5](https://developer.android.com/build/kotlin-support).
* AGP 8.5 is supported from Gradle version [8.7](https://developer.android.com/build/releases/about-agp) and Android Studio Koala [2024.1.1](https://developer.android.com/studio/releases).
## Migration Steps [#migration-steps]
### Rename any references to the `common` package to `verifier` [#rename-any-references-to-the-common-package-to-verifier]
The shared `common` module has been removed and bundled into the Android Verifier SDK. The package path has moved from `mattr.global.mobilecredentials.common` to `mattr.global.mobilecredentials.verifier`. Update all imports accordingly:
```diff
- import mattr.global.mobilecredentials.common.*
+ import mattr.global.mobilecredentials.verifier.*
```
This can be done with a global find and replace across your codebase. If you are using both the Holder and Verifier SDKs, you will need to limit your search to the relevant parts of your application.
### Remove the Common dependency from your project [#remove-the-common-dependency-from-your-project]
The Common dependency is now bundled into the Verifier SDK. Remove it from your project's `build.gradle` or `build.gradle.kts` file:
```diff
dependencies {
implementation("global.mattr.mobilecredentials:verifier:6.0.0")
- implementation("global.mattr.mobilecredentials:common:5.x.x")
}
```
### Update `sendProximityPresentationRequest` calls [#update-sendproximitypresentationrequest-calls]
The `skipStatusCheck` parameter has been renamed to `checkStatus` with inverted semantics. When `checkStatus` is `true` (default), the SDK will verify credential status. When `false`, it will skip status checking. Update all calls accordingly:
```diff
- val response = verifier.sendProximityPresentationRequest(request = requests, skipStatusCheck = true)
+ val response = verifier.sendProximityPresentationRequest(request = requests, checkStatus = false)
```
| Old Parameter | New Parameter | Mapping |
| ----------------------------------- | ------------------------------ | ------------------ |
| `skipStatusCheck = false` (default) | `checkStatus = true` (default) | No change needed |
| `skipStatusCheck = true` | `checkStatus = false` | Invert the boolean |
# Android Verifier SDK v7.0.0 Migration Guide
URL: /docs/verification/remote-mobile-verifiers/sdks/android-7.0.0-migration-guide
Description: A comprehensive guide to migrating to Android Verifier SDK v7.0.0, covering breaking changes, new features, and step-by-step migration instructions.
## Overview [#overview]
This guide provides a comprehensive overview of the changes introduced in the Android Verifier SDK
v7.0.0, including breaking changes, new features, and migration steps.
This release focuses on strengthening trust between your verifier application and your MATTR VII
tenant, improving consistency across platforms, and making verification results more predictable. The
headline change is **SDK Tethering**, which becomes **required** in this release: every SDK and app
instance is now registered with, and licensed by, your MATTR VII tenant at initialization.
Unlike the Holder SDK, where SDK Tethering is optional, **SDK Tethering is required for the Verifier
SDK** from v7.0.0. This builds on an existing requirement — remote mobile (app-to-app) verification
already required you to supply a `platformConfiguration` so the SDK could reach your MATTR VII tenant
to handle the backend verification. That `platformConfiguration` is now mandatory for all
initializations and additionally drives SDK Tethering.
## Key Features [#key-features]
* **SDK Tethering (required)**: The Android Verifier SDK is now tethered to a MATTR VII tenant, tying
each SDK/app instance to your tenant. On first initialization the SDK registers the app instance
with the tenant specified in `PlatformConfiguration` and obtains a license; on subsequent
initializations the existing license is renewed automatically. This lets you view registered and
active app instances directly from your tenant for operational insight, and establishes a remote
management channel we expect to extend in future releases (for example, remote syncing of trusted
issuer lists and eventing). The SDK uses [Key Attestation](https://source.android.com/docs/security/features/keystore/attestation)
during app registration. Network access is required when registration or renewal is performed.
* **Cross-platform alignment**: Verification result types and the revocation status list API have
been renamed and restructured to align with the iOS Verifier SDK, minimizing divergence for teams
maintaining cross-platform applications.
* **More predictable session results**: `OnlinePresentationSessionResult` and the revocation status
list refresh result are now sealed interfaces with explicit `Success` and `Failure` variants,
removing ambiguity from result handling.
* **Simpler remote mobile configuration**: The application ID for remote mobile (app-to-app)
verification is now taken from `PlatformConfiguration`, so it no longer needs to be passed on every
request.
* **General stability and performance improvements**: Multiple refinements reduce integration
friction, increase consistency, and improve overall reliability.
## Breaking Changes [#breaking-changes]
This section outlines the breaking changes introduced in v7.0.0 that require updates to your existing
implementation:
| # | Change | Impact |
| - | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | SDK Tethering is now required: `PlatformConfiguration` is mandatory on `initialize`, which now also registers the app instance and obtains a license | Always supply a `PlatformConfiguration`. Handle the new `InvalidLicenseException` and `FailedToRegisterException`, which can be thrown by `initialize` and by most SDK APIs. |
| 2 | `MobileCredentialVerifier.updateTrustedIssuerStatusLists` renamed to `refreshRevocationStatusLists` | Update all references to the new method name. |
| 3 | `MobileCredentialVerifier.getTrustedIssuerStatusListsCacheInfo` renamed to `getRevocationStatusListsCacheInfo` | Update all references to the new method name. |
| 4 | `TrustedIssuerStatusListsCacheInfo` renamed to `RevocationStatusListsCacheInfo` | Update all code that constructs or references this type. |
| 5 | `UpdateTrustedIssuerStatusListsResult` renamed to `RevocationStatusListsRefreshResult` and converted to a sealed interface; the `.success: Boolean` field is removed | Replace `if (result.success)` with `when`/`is` pattern matching over `Success` and `Failure`. |
| 6 | `OnlinePresentationSessionResult` converted to a sealed interface with `Success` and `Failure` variants | Replace field-based branching with `when`/`is` pattern matching. |
| 7 | `MobileCredentialResponse.credentials` and `MobileCredentialResponse.credentialErrors` are now required non-nullable `List` fields | Remove null-check branches and `?.` navigation; both arguments must be provided at construction. |
| 8 | `applicationId` parameter removed from `requestMobileCredentials` (remote mobile, app-to-app) | Remove the `applicationId` argument and supply it via `PlatformConfiguration` instead. |
## Migration Steps [#migration-steps]
### Create a verifier application on your MATTR VII tenant [#create-a-verifier-application-on-your-mattr-vii-tenant]
SDK Tethering requires a verifier application configured on the MATTR VII tenant your SDK connects
to. If you already use remote mobile (app-to-app) verification you will have created one; the same
application is reused for tethering. If you have not, create one now.
To register your Android application, make a request to create a verifier application:
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "My Android Verifier Application",
"type": "android",
"packageName": "com.yourcompany.verifierapp",
"packageSigningCertificateThumbprints": [
"1232584b6f6a892d356899fb9576c5f226a179e6199f2b7a1d837b5c234c5a8e"
]
}
```
* `name`: A unique name to identify your verifier application.
* `type`: Must be `android` for an Android application.
* `packageName`: The package name of your Android application.
* `packageSigningCertificateThumbprints`: SHA-256 hex-encoded fingerprints of the signing key
certificates used to sign your APK or app bundle. This ensures the tenant only accepts requests
from known and trusted applications. Refer to [Android app signing](/docs/verification/android-app-signing)
for more information.
The response will include a unique `id` for your application, used by the SDK to identify and
authenticate your application.
### Supply `PlatformConfiguration` and handle tethering errors at initialization [#supply-platformconfiguration-and-handle-tethering-errors-at-initialization]
`PlatformConfiguration` is now required on `initialize`. Previously it was optional and only used for
remote mobile (app-to-app) verification flows; it now also drives SDK Tethering, registering the app
instance with your MATTR VII tenant and obtaining a license on first initialization.
Always pass a `PlatformConfiguration`, and handle the new `InvalidLicenseException` and
`FailedToRegisterException` that `initialize` can now throw:
```diff
- val platformConfiguration = PlatformConfiguration(
- tenantHost = URL("https://your-tenant.vii.mattr.global")
- )
- MobileCredentialVerifier.initialize(context, platformConfiguration)
+ val platformConfiguration = PlatformConfiguration(
+ tenantHost = URL("https://your-tenant.vii.mattr.global"),
+ applicationId = "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
+ )
+ try {
+ MobileCredentialVerifier.initialize(context, platformConfiguration)
+ } catch (e: FailedToRegisterException) {
+ // Registration with the MATTR VII tenant failed — check connectivity and configuration
+ } catch (e: InvalidLicenseException) {
+ // The SDK license is missing, invalid, or expired
+ }
```
* `tenantHost`: The URL of your MATTR VII tenant where your verifier application is configured.
* `applicationId`: The `id` of your configured MATTR VII verifier application.
The majority of the SDK's other APIs can now also throw `InvalidLicenseException` when a valid
license is not present. Network access is required the first time the SDK initializes (for
registration) and when the license is renewed on subsequent initializations. Update your error
handling, logging, analytics, and support diagnostics to account for these cases.
### Remove the `applicationId` argument from `requestMobileCredentials` [#remove-the-applicationid-argument-from-requestmobilecredentials]
`MobileCredentialVerifier.requestMobileCredentials` (remote mobile, app-to-app) no longer takes an
`applicationId` parameter. It now uses the application ID supplied in `PlatformConfiguration` during
initialization. Remove the argument from your call sites:
```diff
val result = verifier.requestMobileCredentials(
request = listOf(mobileCredentialRequest),
challenge = challenge,
- applicationId = "your-application-id"
)
```
Ensure you provide `applicationId` via `PlatformConfiguration` (see the previous step) instead.
### Update revocation status list method and type names [#update-revocation-status-list-method-and-type-names]
The revocation status list management API has been renamed from `TrustedIssuer`-prefixed terminology
to `Revocation` terminology to better reflect its purpose — managing the lists used to check the
revocation status of credentials. Update all call sites:
```diff
- val result = verifier.updateTrustedIssuerStatusLists()
+ val result = verifier.refreshRevocationStatusLists()
- val cacheInfo = verifier.getTrustedIssuerStatusListsCacheInfo()
+ val cacheInfo = verifier.getRevocationStatusListsCacheInfo()
```
| Old | New |
| ---------------------------------------- | ------------------------------------- |
| `updateTrustedIssuerStatusLists()` | `refreshRevocationStatusLists()` |
| `getTrustedIssuerStatusListsCacheInfo()` | `getRevocationStatusListsCacheInfo()` |
| `TrustedIssuerStatusListsCacheInfo` | `RevocationStatusListsCacheInfo` |
| `UpdateTrustedIssuerStatusListsResult` | `RevocationStatusListsRefreshResult` |
### Update revocation status list refresh result handling [#update-revocation-status-list-refresh-result-handling]
`RevocationStatusListsRefreshResult` (formerly `UpdateTrustedIssuerStatusListsResult`) has been
converted from a data class to a sealed interface with `Success` and `Failure` variants, and the
`.success: Boolean` field has been removed. Replace boolean branching with `when`/`is` pattern
matching:
```diff
- val result = verifier.refreshRevocationStatusLists()
- if (result.success) {
- // Handle success
- } else {
- // Handle failure
- }
+ when (val result = verifier.refreshRevocationStatusLists()) {
+ is RevocationStatusListsRefreshResult.Success -> {
+ // Handle successful refresh
+ }
+ is RevocationStatusListsRefreshResult.Failure -> {
+ // result.failedLists is available here
+ }
+ }
```
### Update online presentation session result handling [#update-online-presentation-session-result-handling]
`OnlinePresentationSessionResult` has been converted from a data class to a sealed interface with
`Success` and `Failure` variants. Replace field-based branching with `when`/`is` pattern matching:
```diff
- val result = ... // OnlinePresentationSessionResult
- if (result.mobileCredentialResponse != null) {
- // Use result.mobileCredentialResponse
- } else {
- // Use result.error
- }
+ when (result) {
+ is OnlinePresentationSessionResult.Success -> {
+ // result.mobileCredentialResponse is guaranteed
+ }
+ is OnlinePresentationSessionResult.Failure -> {
+ // result.error is guaranteed
+ }
+ }
```
### Remove optional handling on `MobileCredentialResponse` collections [#remove-optional-handling-on-mobilecredentialresponse-collections]
`MobileCredentialResponse.credentials` and `MobileCredentialResponse.credentialErrors` are now
required non-nullable `List` fields, instead of nullable fields. Remove null checks and `?.`
navigation, and provide both arguments when constructing the type:
```diff
- for (credential in response.credentials.orEmpty()) { ... }
+ for (credential in response.credentials) { ... }
```
# iOS Verifier SDK v5.0.0 Migration Guide
URL: /docs/verification/remote-mobile-verifiers/sdks/ios-5.0.0-migration-guide
## Overview [#overview]
This guide provides a comprehensive overview of the changes introduced in MobileCredentialVerifierSDK v5.0.0 for iOS, including breaking changes, new features, and migration steps.
## Key Features [#key-features]
* **Lifecycle management improvements**: New `destroy()` method enables complete SDK reset, plus `deinitialize()` no longer requires `@MainActor` context for improved flexibility.
* **Improved reliability in contactless flows**: Enhanced BLE performance delivers more consistent proximity credential exchanges and faster engagements.
* **Enhanced storage reliability**: Fixed intermittent storage initialization errors during app launch by migrating key storage from `UserDefaults` to the Keychain with explicit availability checks.
* **Better timeout handling**: Proximity presentation session timeouts are now correctly propagated to the caller instead of being silently ignored.
For a detailed list of changes included in this release, refer to the [SDK Changelog](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/changelog).
## Breaking Changes [#breaking-changes]
This section outlines the breaking changes introduced in v5.0.0 that require updates to your existing implementation:
| # | Symbol | Change | Impact |
| - | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------- |
| 1 | `MobileCredentialVerifier.sendProximityPresentationRequest(request:skipStatusCheck:)` | Parameter renamed to `checkStatus:` with **inverted semantics** | All call sites using `skipStatusCheck:` must be updated. |
| 2 | `MobileCredentialVerifier.deinitialize()` | No longer `@MainActor` | Can now be called from any actor context. |
| 3 | `MobileCredentialVerifierError` | Introduced two new enum cases: `.storageInitializedInBackground`, `.sdkInitialized` | Exhaustive `switch` statements must add new cases. |
## New Additions [#new-additions]
### Methods [#methods]
| Method | Purpose |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `destroy()` | Destroys SDK instance and deletes all certificates from storage. Throws `sdkInitialized` if called while SDK is initialized. |
### Enum Cases [#enum-cases]
| Type | New Case | Description |
| ------------------------------- | --------------------------------- | --------------------------------------------------------------------- |
| `MobileCredentialVerifierError` | `.storageInitializedInBackground` | SDK initialized without keychain access (e.g., during app prewarming) |
| `MobileCredentialVerifierError` | `.sdkInitialized` | Operation requires SDK to be deinitialized first |
## Deprecations [#deprecations]
No new deprecations.
## Bug Fixes [#bug-fixes]
* Fixed intermittent "unable to initialize storage" errors during app launch. The issue was caused by using `UserDefaults` for `keyId` storage, which does not guarantee persistence—particularly during iOS app prewarming when protected data may be unavailable. The SDK now stores the `keyId` in the Keychain and performs explicit availability checks before initialization, throwing a new `storageInitializedInBackground` error when the keychain is inaccessible.
* Fixed an issue where `ProximityPresentationSession` timeouts were silently ignored instead of throwing an error. The SDK now correctly propagates timeout errors to the caller when the session times out waiting for a response.
## Minimum Requirements [#minimum-requirements]
* iOS 15+ for core SDK functionality.
## Migration Steps [#migration-steps]
### Update `sendProximityPresentationRequest` calls [#update-sendproximitypresentationrequest-calls]
The `skipStatusCheck` parameter has been renamed to `checkStatus` with inverted semantics. When `checkStatus` is `true` (default), the SDK will verify credential status. When `false`, it will skip status checking. Update all calls accordingly:
```diff
- let response = try await verifier.sendProximityPresentationRequest(request: requests, skipStatusCheck: true)
+ let response = try await verifier.sendProximityPresentationRequest(request: requests, checkStatus: false)
```
| Old Parameter | New Parameter | Mapping |
| ---------------------------------- | ----------------------------- | ------------------ |
| `skipStatusCheck: false` (default) | `checkStatus: true` (default) | No change needed |
| `skipStatusCheck: true` | `checkStatus: false` | Invert the boolean |
### Handle new error cases [#handle-new-error-cases]
If you have exhaustive `switch` statements on `MobileCredentialVerifierError`, you must add handlers for the two new error cases:
```diff
switch error {
case .sdkNotInitialized:
// Handle not initialized
case .storageInitialization:
// Handle storage error
+ case .storageInitializedInBackground:
+ // SDK initialized during app prewarming — prompt user to retry
+ case .sdkInitialized:
+ // Operation requires deinitialize() first
// ... other cases
}
```
**Error handling guidance:**
* `.storageInitializedInBackground`: This error occurs when the SDK is initialized during iOS app prewarming, before the keychain is accessible. Listen for `UIApplication.protectedDataDidBecomeAvailableNotification` and re-initialize the SDK when keychain access becomes available.
* `.sdkInitialized`: This error is thrown by `destroy()` when called while the SDK is still initialized. Call `deinitialize()` first, then retry the operation.
### Use `destroy()` for complete reset (optional) [#use-destroy-for-complete-reset-optional]
If you need to completely reset SDK state and delete all stored certificates and credentials, you can now call the new `destroy()` method. This is optional and should be used with caution as it will remove all data:
```swift
// Ensure SDK is deinitialized first
await MobileCredentialVerifier.shared.deinitialize()
// Then destroy all stored data
try MobileCredentialVerifier.shared.destroy()
// Re-initialize when needed
try MobileCredentialVerifier.shared.initialize()
```
### Handle `deinitialize()` actor context change [#handle-deinitialize-actor-context-change]
The `deinitialize()` method is no longer restricted to `@MainActor`. You can now call it from any context without dispatching to the main actor:
```diff
- await MainActor.run {
- await MobileCredentialVerifier.shared.deinitialize()
- }
+ await MobileCredentialVerifier.shared.deinitialize()
```
# iOS Verifier SDK v6.0.0 Migration Guide
URL: /docs/verification/remote-mobile-verifiers/sdks/ios-6.0.0-migration-guide
Description: A comprehensive guide to migrating to iOS Verifier SDK v6.0.0, covering breaking changes, new features, and step-by-step migration instructions.
## Overview [#overview]
This guide provides a comprehensive overview of the changes introduced in the iOS Verifier SDK
v6.0.0, including breaking changes, new features, and migration steps.
This release focuses on strengthening trust between your verifier application and your MATTR VII
tenant, improving consistency across platforms, and making verification results more predictable. The
headline change is **SDK Tethering**, which becomes **required** in this release: every SDK and app
instance is now registered with, and licensed by, your MATTR VII tenant at initialization.
Unlike the Holder SDK, where SDK Tethering is optional, **SDK Tethering is required for the Verifier
SDK** from v6.0.0. This builds on an existing requirement — remote mobile (app-to-app) verification
already required you to supply a `platformConfiguration` so the SDK could reach your MATTR VII tenant
to handle the backend verification. That `platformConfiguration` is now mandatory for all
initializations and additionally drives SDK Tethering.
## Key Features [#key-features]
* **SDK Tethering (required)**: The iOS Verifier SDK is now tethered to a MATTR VII tenant, tying
each SDK/app instance to your tenant. On first initialization the SDK registers the app instance
with the tenant specified in `PlatformConfiguration` and obtains a license; on subsequent
initializations the existing license is renewed automatically. This lets you view registered and
active app instances directly from your tenant for operational insight, and establishes a remote
management channel we expect to extend in future releases (for example, remote syncing of trusted
issuer lists and eventing). The SDK uses [App Attest](https://developer.apple.com/documentation/devicecheck/establishing-your-app-s-integrity)
during app registration when available on the device. Network access is required when registration
or renewal is performed.
* **Asynchronous initialization**: `initialize` is now asynchronous, aligning the SDK with modern
Swift concurrency and the registration/licensing work performed during tethering.
* **Cross-platform alignment**: Verification result types and the revocation status list API have
been renamed and restructured to align with the Android Verifier SDK, minimizing divergence for
teams maintaining cross-platform applications.
* **Simpler challenge handling for remote verification**: The `challenge` parameter for remote mobile
(app-to-app) verification is now optional; when omitted, the SDK generates a cryptographically
secure challenge for you.
* **Clearer connectivity error reporting**: Remote mobile verification now surfaces a dedicated
`connectivityError` when the internet connection is lost mid-flow.
* **General stability and performance improvements**: Multiple refinements reduce integration
friction, increase consistency, and improve overall reliability.
## Breaking Changes [#breaking-changes]
This section outlines the breaking changes introduced in v6.0.0 that require updates to your existing
implementation:
| # | Change | Impact |
| -- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | SDK Tethering is now required: `platformConfiguration` is mandatory on `initialize`, which now also registers the app instance and obtains a license | Always supply a `platformConfiguration`. Handle the new `invalidLicense` and `failedToRegister` errors, which can be thrown by `initialize` and by most SDK APIs. |
| 2 | `initialize` is now asynchronous | Add `await` to all call sites and call `initialize` from an asynchronous context. |
| 3 | `MobileCredentialVerifierError.platformConfigurationInvalid` removed | Remove handling for this case; `fetchAppleWalletConfiguration(request:merchantId:)` no longer throws it. |
| 4 | `VerificationResult` renamed to `MobileCredentialVerificationResult`, with `.reason` renamed to `.failureType` | Update all type references, rename `.reason` to `.failureType`, and remove use of `VerificationFailedReason`. |
| 5 | `TrustedCertificateVerificationResult.reason` renamed to `.failureType` | Rename `.reason` to `.failureType` and remove use of `VerificationFailedReason`. |
| 6 | `MobileCredentialVerificationFailureType` now serializes as a `{type, message}` object instead of a plain raw-value string | Update any storage or transport layer that persists or forwards these serialized values. |
| 7 | `TrustedCertificateVerificationFailureType` now serializes as a `{type, message}` object instead of a plain raw-value string | Update any storage or transport layer that persists or forwards these serialized values. |
| 8 | Revocation status list methods and types renamed from `TrustedIssuer`-prefixed terminology to `Revocation` terminology | Rename `updateTrustedIssuerStatusLists` → `refreshRevocationStatusLists`, `getTrustedIssuerStatusListsCacheInfo` → `getRevocationStatusListsCacheInfo`, and the corresponding return types. |
| 9 | `RevocationStatusListsRefreshResult` and `OnlinePresentationSessionResult` converted from structs with optional properties to `@frozen` enums with `success` and `failure` cases | Replace property-based branching with `switch`/`case` pattern matching. |
| 10 | `applicationId` parameter removed from `fetchAppleWalletConfiguration` and `requestMobileCredentials` | Remove the `applicationId` argument from these call sites; the SDK now uses the `applicationId` from `PlatformConfiguration`. |
## Migration Steps [#migration-steps]
### Create a verifier application on your MATTR VII tenant [#create-a-verifier-application-on-your-mattr-vii-tenant]
SDK Tethering requires a verifier application configured on the MATTR VII tenant your SDK connects
to. If you already use remote mobile (app-to-app) verification you will have created one; the same
application is reused for tethering. If you have not, create one now.
To register your iOS application, make a request to create a verifier application:
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "My iOS Verifier Application",
"type": "ios",
"bundleId": "com.yourcompany.verifierapp",
"teamId": "YOUR_APPLE_TEAM_ID"
}
```
* `name`: A unique name to identify your verifier application.
* `type`: Must be `ios` for an iOS application.
* `bundleId`: The Bundle ID of your iOS app (must match your Xcode project configuration).
* `teamId`: Your Apple Developer Team ID.
The response will include a unique `id` for your application. This is the `applicationId` you supply
in `PlatformConfiguration` at initialization, which the SDK now uses for all flows (including remote
mobile verification).
### Supply `platformConfiguration` and make `initialize` asynchronous [#supply-platformconfiguration-and-make-initialize-asynchronous]
`initialize` is now asynchronous and `platformConfiguration` is required. Previously,
`platformConfiguration` was optional and only used for remote mobile (app-to-app) verification flows;
it now also drives SDK Tethering, registering the app instance with your MATTR VII tenant and
obtaining a license on first initialization.
Add `await`, call `initialize` from an asynchronous context, and always pass a
`platformConfiguration`:
```diff
- let platformConfiguration = PlatformConfiguration(
- tenantHost: URL(string: "https://your-tenant.vii.mattr.global")!
- )
- try MobileCredentialVerifier.shared.initialize(platformConfiguration: platformConfiguration)
+ let platformConfiguration = PlatformConfiguration(
+ tenantHost: URL(string: "https://your-tenant.vii.mattr.global")!,
+ applicationId: "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
+ )
+ try await MobileCredentialVerifier.shared.initialize(platformConfiguration: platformConfiguration)
```
* `tenantHost`: The URL of your MATTR VII tenant where your verifier application is configured.
* `applicationId`: The `id` of your configured iOS Verifier Application.
Network access is required the first time the SDK initializes (for registration) and when the
license is renewed on subsequent initializations.
### Handle license and registration errors [#handle-license-and-registration-errors]
Because tethering registers and licenses the SDK, `initialize` can now throw
`MobileCredentialVerifierError.invalidLicense` and `MobileCredentialVerifierError.failedToRegister`.
The majority of the SDK's other APIs can now also throw `invalidLicense` when a valid license is not
present. Update your error handling, logging, analytics, and support diagnostics to account for these
cases:
```diff
do {
try await MobileCredentialVerifier.shared.initialize(platformConfiguration: platformConfiguration)
} catch {
switch error {
+ case MobileCredentialVerifierError.failedToRegister:
+ // Registration with the MATTR VII tenant failed — check connectivity and configuration
+ case MobileCredentialVerifierError.invalidLicense:
+ // The SDK license is missing, invalid, or expired
// ... other cases
}
}
```
The `MobileCredentialVerifierError.platformConfigurationInvalid` case has been removed and is no
longer thrown by `fetchAppleWalletConfiguration(request:merchantId:)`. Remove any handling for it.
### Update `VerificationResult` to `MobileCredentialVerificationResult` [#update-verificationresult-to-mobilecredentialverificationresult]
The `VerificationResult` type has been renamed to `MobileCredentialVerificationResult` and aligned
structurally with Android. Its `reason` property has been renamed to `failureType`, typed directly as
`MobileCredentialVerificationFailureType?` rather than the now-removed `VerificationFailedReason`
wrapper. `MobileCredential.verificationResult` and `MobileCredentialPresentation.verificationResult`
now return `MobileCredentialVerificationResult`:
```diff
- let result: VerificationResult = credential.verificationResult
+ let result: MobileCredentialVerificationResult = credential.verificationResult
- let failure = result.reason
+ let failure = result.failureType
```
Replace all references to `VerificationResult` with `MobileCredentialVerificationResult`, rename
`.reason` to `.failureType`, and remove any usage of `VerificationFailedReason`.
### Rename `TrustedCertificateVerificationResult.reason` to `failureType` [#rename-trustedcertificateverificationresultreason-to-failuretype]
The same `.reason` → `.failureType` rename applies to `TrustedCertificateVerificationResult`. Its
`failureType` is now typed directly as `TrustedCertificateVerificationFailureType?` instead of the
now-removed `VerificationFailedReason` wrapper:
```diff
- let failure = trustedCertificateResult.reason
+ let failure = trustedCertificateResult.failureType
```
### Update failure-type serialization handling [#update-failure-type-serialization-handling]
`MobileCredentialVerificationFailureType` and `TrustedCertificateVerificationFailureType` now encode
and decode as a `{type, message}` object instead of a plain raw-value string:
```diff
- "TrustedIssuerCertificateNotFound"
+ {"type": "TrustedIssuerCertificateNotFound", "message": "Trusted issuer certificate not found"}
```
If you persist or forward the serialized value of either failure type, update your storage or
transport layer to produce and consume the new format.
### Update revocation status list method and type names [#update-revocation-status-list-method-and-type-names]
The revocation status list management API has been renamed from `TrustedIssuer`-prefixed terminology
to `Revocation` terminology to better reflect its purpose — managing the lists used to check the
revocation status of credentials. Update all call sites to use the new method names and return types:
```diff
- let result = try await verifier.updateTrustedIssuerStatusLists()
+ let result = try await verifier.refreshRevocationStatusLists()
- let cacheInfo = verifier.getTrustedIssuerStatusListsCacheInfo()
+ let cacheInfo = try verifier.getRevocationStatusListsCacheInfo()
```
| Old | New |
| ---------------------------------------- | ------------------------------------- |
| `updateTrustedIssuerStatusLists()` | `refreshRevocationStatusLists()` |
| `getTrustedIssuerStatusListsCacheInfo()` | `getRevocationStatusListsCacheInfo()` |
| `UpdateTrustedIssuerStatusListsResult` | `RevocationStatusListsRefreshResult` |
| `TrustedIssuerStatusListsCacheInfo` | `RevocationStatusListsCacheInfo` |
### Update result-type handling for `@frozen` enums [#update-result-type-handling-for-frozen-enums]
`RevocationStatusListsRefreshResult` and `OnlinePresentationSessionResult` have been converted from
structs with optional properties to `@frozen` enums with `success` and `failure` cases. Replace
property-based branching with `switch`/`case` pattern matching.
`RevocationStatusListsRefreshResult.success` carries `nextUpdate: Date?`, and `.failure` carries
`nextUpdate: Date?` and `failedLists: [String: [String]]`:
```diff
- let result = try await verifier.refreshRevocationStatusLists()
- if result.success {
- // Handle success
- } else {
- // Handle failure
- }
+ switch try await verifier.refreshRevocationStatusLists() {
+ case .success(let nextUpdate):
+ // All status lists refreshed; schedule the next refresh before nextUpdate
+ case .failure(let nextUpdate, let failedLists):
+ // failedLists holds the URIs that failed to refresh, keyed by trusted issuer certificate ID
+ }
```
`OnlinePresentationSessionResult.success` carries `sessionId: String`, `challenge: String?`, and
`mobileCredentialResponse: MobileCredentialResponse?`; `.failure` carries `sessionId: String`,
`challenge: String?`, and `error: OnlinePresentationResultError`:
```diff
- if let response = result.mobileCredentialResponse {
- // Use response
- } else {
- // Use result.error
- }
+ switch result {
+ case .success(_, _, let mobileCredentialResponse):
+ // mobileCredentialResponse?.credentials holds the presented credentials
+ case .failure(_, _, let error):
+ // error.type and error.message describe the failure
+ }
```
### Remove the `applicationId` argument from `fetchAppleWalletConfiguration` and `requestMobileCredentials` [#remove-the-applicationid-argument-from-fetchapplewalletconfiguration-and-requestmobilecredentials]
The `applicationId` parameter has been removed from `fetchAppleWalletConfiguration` and
`requestMobileCredentials`. The SDK now uses the `applicationId` supplied in `PlatformConfiguration`
during initialization. Remove the argument from your call sites:
```diff
let result = try await verifier.requestMobileCredentials(
request: [mobileCredentialRequest],
challenge: challenge,
- applicationId: "your-application-id"
)
```
Ensure you provide `applicationId` via `PlatformConfiguration` (see the earlier step) instead.
### (Optional) Simplify challenge handling for remote mobile verification [#optional-simplify-challenge-handling-for-remote-mobile-verification]
The `challenge` parameter in `requestMobileCredentials` for remote mobile (app-to-app) verification
is now optional. When omitted or left blank, the SDK generates a cryptographically secure random
32-byte challenge automatically, so you no longer need to manage challenge generation yourself:
```diff
- let result = try await verifier.requestMobileCredentials(
- request: [mobileCredentialRequest],
- challenge: UUID().uuidString
- )
+ let result = try await verifier.requestMobileCredentials(
+ request: [mobileCredentialRequest]
+ )
```
This is an optional improvement; supplying your own `challenge` continues to work.
### (Optional) Handle connectivity errors in remote mobile verification [#optional-handle-connectivity-errors-in-remote-mobile-verification]
`requestMobileCredentials` for remote mobile (app-to-app) verification now throws
`MobileCredentialVerifierError.connectivityError` if the internet connection is lost during the flow.
Handle this case to provide clear feedback and retry guidance to your users:
```diff
do {
let result = try await verifier.requestMobileCredentials(request: requests)
} catch {
+ if case MobileCredentialVerifierError.connectivityError = error {
+ // Prompt the user to check their connection and retry
+ }
}
```
# Verifier Mobile SDKs Overview
URL: /docs/verification/remote-mobile-verifiers/sdks/overview
## Overview [#overview]
The mDocs Verifier SDK are based on the
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html) and
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) standards, which establish an
interoperable digital representation of mobile-based credentials such as mobile drivers licenses
(mDL). However, these SDKs can extend the same technology and architecture to more than just mDLs,
but rather any conforming mobile document ([mDoc](/docs/concepts/mdocs)) - a term defined in ISO/IEC 18013-5.
The mDocs Verifier SDKs are available for React Native, iOS, and Android. They help developers add
mDocs verification capabilities to their apps, allowing secure and privacy preserving verification
of presented mDocs in various interoperable workflows.
To get started with any of our mDocs Verifier SDKs, please [contact
us](mailto:sales@mattr.global).
## SDK Capabilities [#sdk-capabilities]
The mDocs Verifier SDKs offer tools to assist developers integrating the following capabilities into
their applications:
* Interface with an mDoc holder to request presentations of issued mDocs via:
* Proximity verification: Verify an mDoc presented in-person as per
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
* Remote mobile app verification: Remotely verify an mDoc presented from a different app
installed on the same mobile device (as per
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html)).
* Manage a list of trusted issuer certificates which presented mDocs can be validated against.
* Manage status lists which can be used to check mDocs' [revocation status](/docs/issuance/revocation/overview).
* Interface with an mDoc holder to request presentations of issued mDocs via:
* Proximity verification: Verify an mDoc presented in-person as per
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
* Remote mobile app verification: Remotely verify an mDoc presented from a different app
installed on the same mobile device (as per
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html)).
* Manage a list of trusted issuer certificates which presented mDocs can be validated against.
* Manage status lists which can be used to check mDocs' [revocation status](/docs/issuance/revocation/overview).
* Interface with an mDoc holder to request presentations of issued mDocs via:
* Proximity verification: Verify an mDoc presented in-person as per
[ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html).
* Manage a list of trusted issuer certificates which presented mDocs can be validated against.
* Manage status lists which can be used to check mDocs' [revocation status](/docs/issuance/revocation/overview).
## Supported features [#supported-features]
### Supported ISO/IEC 18013-5 Features [#supported-isoiec-18013-5-features]
Below is a summary of ISO/IEC 18013-5 features supported by the mDocs Verifier SDKs:
| Feature | Supported options | Default |
| :------------------------------ | :----------------------------------------------------------------- | :------------------------------- |
| Device engagement | QR code | QR code |
| Device retrieval data transport | BLE with either `mDocPeripheralServer` or `mDocCentralClient` mode | Determined by holder application |
| Ephemeral session key curve | Any NIST P-\* keys | Determined by holder application |
| IACA public key curves | P-256, P-384, P-521 | Determined by Issuer |
| Device authentication mode | Digital Signature or ECDH-agreed MAC | Determined by holder application |
| Feature | Supported options | Default |
| :------------------------------ | :----------------------------------------------------------------- | :------------------------------- |
| Device engagement | QR code and NFC | QR code |
| Device retrieval data transport | BLE with either `mDocPeripheralServer` or `mDocCentralClient` mode | Determined by holder application |
| Ephemeral session key curve | Any NIST P-\* keys | Determined by holder application |
| IACA public key curves | P-256, P-384, P-521 | Determined by Issuer |
| Device authentication mode | Digital Signature or ECDH-agreed MAC | Determined by holder application |
| Feature | Supported options | Default |
| :------------------------------ | :----------------------------------------------------------------- | :------------------------------- |
| Device engagement | QR code | QR code |
| Device retrieval data transport | BLE with either `mDocPeripheralServer` or `mDocCentralClient` mode | Determined by holder application |
| Ephemeral session key curve | Any NIST P-\* keys | Determined by holder application |
| IACA public key curves | P-256, P-384, P-521 | Determined by Issuer |
| Device authentication mode | Digital Signature or ECDH-agreed MAC | Determined by holder application |
## System requirements [#system-requirements]
The SDK is developed in the [Swift](https://developer.apple.com/swift/) programming language and is
meant for integration into iOS applications developed in Swift and/or Objective-C. Specifically, it
currently only supports applications developed in iOS 15 and above.
This SDK is developed in the Kotlin programming language and is meant for integration into Android
applications. It currently supports Android 7 (API level 24) and above.
This SDK is meant for integration into React Native applications using React Native 0.78 and above.
Supported operating systems are:
* iOS 15 or higher.
* Android 7 or higher.
## Dependencies [#dependencies]
This section lists all dependencies for using mDocs Verifier SDKs.
**Third party dependencies**
* [CBORCoding](https://github.com/SomeRandomiOSDev/CBORCoding) (MIT license).
* [swift-certificates](https://github.com/apple/swift-certificates.git) 1.7.0 (Apache-2.0 License).
* [swift-asn1](https://github.com/apple/swift-asn1) 1.3.1 (Apache-2.0 License).
**Apple frameworks**
* [Security](https://developer.apple.com/documentation/security)
* [CryptoKit](https://developer.apple.com/documentation/cryptokit/)
* [LocalAuthentication](https://developer.apple.com/documentation/localauthentication)
* [CoreBluetooth](https://developer.apple.com/documentation/corebluetooth)
* [Combine](https://developer.apple.com/documentation/combine)
* [OSLog](https://developer.apple.com/documentation/oslog)
* [UIKit](https://developer.apple.com/documentation/uikit)
* [AppKit (on macOS)](https://developer.apple.com/documentation/appkit)
* [PassKit](https://developer.apple.com/documentation/passkit)
* [SwiftUI](https://developer.apple.com/documentation/swiftui)
**Toolchain dependencies**
* Swift 5.10.
* iOS support: The SDK functionality is only available for devices from iOS 15 onwards.
* Xcode: The SDK is built with the latest stable Xcode available in GitHub Actions (setup-xcode action with `latest-stable` label). The current version is Xcode 26.0.0 (17A324).
* macOS 15.
**Kotlin, AGP, Gradle, and Android Studio**
The Android Verifier SDK is built using Kotlin 2.0. This adds some intrinsic dependencies into your build tools.
* Kotlin 2.0 is supported from AGP version [8.5](https://developer.android.com/build/kotlin-support).
* AGP 8.5 is supported from Gradle version [8.7](https://developer.android.com/build/releases/about-agp) and Android Studio Koala [2024.1.1](https://developer.android.com/studio/releases).
**Runtime**
A list of runtime dependencies and licenses is generated at build time and packaged in `res/raw/dependencies_licenses.html`. The majority are Kotlin and Android, the rest are listed below:
* [CBOR-Java](https://github.com/peteroupc/CBOR-Java) - [Public Domain](https://github.com/peteroupc/CBOR-Java/blob/master/LICENSE.md)
* [Timber](https://github.com/JakeWharton/timber) - [Apache 2.0](https://github.com/JakeWharton/timber/blob/trunk/LICENSE.txt)
**Standard libraries**
* [androidx.activity:activity-ktx:1.9.0](https://developer.android.com/jetpack/androidx/releases/activity#1.9.0)
* [androidx.annotation:annotation:1.8.1](https://developer.android.com/jetpack/androidx/releases/annotation#1.8.1)
* [androidx.appcompat:appcompat:1.7.0](https://developer.android.com/jetpack/androidx/releases/appcompat#1.7.0)
* [androidx.biometric:biometric-ktx:1.2.0-alpha05](https://developer.android.com/jetpack/androidx/releases/biometric#1.2.0-alpha05)
* [androidx.browser:browser:1.8.0](https://developer.android.com/jetpack/androidx/releases/browser#1.8.0)
* [androidx.core:core-ktx:1.15.0](https://developer.android.com/jetpack/androidx/releases/core#1.15.0)
* [androidx.credentials:credentials-play-services-auth:1.5.0](https://developer.android.com/jetpack/androidx/releases/credentials#1.5.0)
* [androidx.credentials:credentials:1.5.0](https://developer.android.com/jetpack/androidx/releases/credentials#1.5.0)
* [androidx.fragment:fragment:1.5.7](https://developer.android.com/jetpack/androidx/releases/fragment#1.5.7)
* [org.jetbrains.kotlin:kotlin-reflect:1.9.22](https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-reflect/1.9.22)
* [org.jetbrains.kotlin:kotlin-stdlib:2.0.0](https://kotlinlang.org/)
* [org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3](https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.7.3)
* [org.jetbrains.kotlinx:kotlinx-datetime:0.4.0](https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-datetime-jvm/0.4.0)
* [org.jetbrains.kotlinx:kotlinx-io-bytestring:0.6.0](https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-io-bytestring-tvosarm64/0.6.0)
* [org.jetbrains.kotlinx:kotlinx-io-core:0.6.0](https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-io-core/0.6.0)
* [org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3](https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-serialization-json/1.6.3)
**Third-party libraries**
* [com.jakewharton.timber:timber:5.0.1](https://mvnrepository.com/artifact/com.jakewharton.timber/timber/5.0.1)
* [com.upokecenter:cbor:4.5.2](https://mvnrepository.com/artifact/com.upokecenter/cbor/4.5.2)
* None.
## Versions [#versions]
Below are the available versions of the mDocs Verifier Mobile SDKs, including the current active
version, supported versions, and those that have reached end-of-life (EOL).
| Major version | Status | Latest release | End of Life date | Documentation |
| ------------- | ----------- | -------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| v6 | Active | 6.0.0 | - | [SDK Docs](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/6.0.0/documentation/mobilecredentialverifiersdk/) |
| v5 | Maintenance | 5.1.1 | 26-09-2026 | [SDK Docs](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/5.1.1/documentation/mobilecredentialverifiersdk/) |
| v4 | End of Life | 4.1.0 | 13-05-2026 | - |
| v3 | End of Life | 3.0.0 | 7-10-2025 | - |
| v2 | End of Life | 2.0.0 | 26-08-2025 | - |
| v1 | End of Life | 1.0.1 | 05-05-2025 | - |
| Major version | Status | Latest release | End of Life date | Documentation |
| ------------- | ----------- | -------------- | ---------------- | -------------------------------------------------------------------------------------------- |
| v7 | Active | 7.0.0 | - | [SDK Docs](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/7.0.0/) |
| v6 | Maintenance | 6.1.1 | 26-09-2026 | [SDK Docs](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/6.1.1/) |
| v5 | End of Life | 5.3.2 | 13-05-2026 | - |
| v4 | End of Life | 4.1.1 | 30-12-2025 | - |
| v3 | End of Life | 3.0.0 | 7-10-2025 | - |
| v2 | End of Life | 2.0.0 | 26-08-2025 | - |
| v1 | End of Life | 1.0.1 | 05-05-2025 | - |
| Major version | Status | Latest release | End of Life date | Documentation |
| ------------- | ----------- | -------------- | ---------------- | ----------------------------------------------------------------------------------------------------------- |
| v9 | Active | 9.0.3 | - | [SDK Docs](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/9.0.3/index.html) |
| v8 | End of Life | 8.1.1 | 23-06-2026 | - |
| v7 | End of Life | 7.1.0 | 12-12-2025 | - |
| v6 | End of Life | 6.0.0 | 26-08-2025 | - |
| v5 | End of Life | 5.0.0 | 05-05-2025 | - |
| v4 | End of Life | 4.1.1 | 13-04-2025 | - |
| v3 | End of Life | 3.0.0 | 15-02-2025 | - |
| v2 | End of Life | 2.0.0 | 17-12-2024 | - |
| v1 | End of Life | 1.0.1 | 05-07-2024 | - |
Release candidates (RC) are pre-release versions that may contain new features or changes that are
not yet fully tested. They are intended for testing purposes, are not subject to our SLA and should
not be used in production environments.
# React Native Verifier SDK v9.0.0 Migration Guide
URL: /docs/verification/remote-mobile-verifiers/sdks/react-native-9.0.0-migration-guide
Description: A guide to help developers migrate from React Native Verifier SDK v8.x to v9.0.0, including breaking changes, new features, and best practices.
## Overview [#overview]
This guide provides a comprehensive overview of the changes introduced in React Native Verifier SDK v9.0.0, including breaking changes, new features, and migration steps.
## Key Features [#key-features]
* **App to app verification**: The React Native Verifier SDK can now be used to request credentials from another app on the same device using OID4VP. This allows you to build verification flows directly into your apps and have a holder app on the same device respond.
* **Verify with Apple Wallet (iOS Only)**: The SDK can now request and verify a credential directly from an Apple Wallet using the Verify with Wallet API. Only available for iOS 16 and above.
* **Improved reliability in contactless flows**: Enhanced BLE performance delivers more consistent proximity credential exchanges and faster engagements.
* **Status Lists Draft 14 Support**: The SDK now supports the Token Status List Draft 14 specification while maintaining existing support for Draft 3.
* **Stronger cryptography and standards alignment**: Updated COSE algorithms (as per RFC 9864) strengthen cryptographic compatibility and ensure continued compliance with evolving standards.
* **General stability and performance improvements**: Multiple refinements reduce integration friction, increase consistency across mobile environments, and improve overall user experience.
For a detailed list of changes included in this release, refer to the [SDK Changelog](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/index.html#md:change-log).
## Breaking Changes [#breaking-changes]
| # | Element | Change | Impact |
| - | ------------------------------------------------------ | ------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| 1 | `initialize()` | Now accepts options and returns a `Result` type. | All call sites must handle the result and possible errors. |
| 2 | `sendProximityPresentationRequest` | Option renamed from `skipStatusCheck` to `checkStatus` with inverted semantics. | All call sites using `skipStatusCheck` must be updated. |
| 3 | `ProximityPresentationSessionTerminationErrorType` | New `Exception` value added. | Exhaustive switches must handle new case. |
| 4 | NFC error listener in `registerForNfcDeviceEngagement` | Now routes parse failures to `onError` instead of rethrowing. | Update error handling logic accordingly. |
## New Additions [#new-additions]
### Functions [#functions]
| Function | Description | Platform |
| ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
| `fetchAppleWalletConfiguration(options)` | Fetches Apple Wallet configuration needed before initiating the Apple Wallet verification flow. Returns an `AppleWallet` object. | iOS only |
| `handleDeepLink(options)` | Handles a deep link URL to continue the online presentation flow. | iOS only |
| `requestMobileCredentials(options)` | Remote app-to-app credential verification via Digital Credential Manager / OID4VP. Returns `OnlinePresentationSessionResult`. | Android (DCM/OID4VP), iOS (OID4VP only) |
| `destroy()` | Destroys the verifier SDK instance. Returns an error if called while the SDK is initialized; deinitialize the SDK before calling. | All |
| `getCurrentLogFilePath()` | Returns path to the Verifier SDK log file. | All |
### Updated Function Signatures [#updated-function-signatures]
* `initialize(options?)` — new `InitializeOptions` parameter:
* `loggerConfiguration?: LoggerConfiguration` — `logLevel`, `callbackLogLevel`, optional `logDir`, optional `callback`
* `platformConfiguration?: PlatformConfiguration` — `tenantHost: string` (required for `requestMobileCredentials`)
### New initialize Options [#new-initialize-options]
* `loggerConfiguration?: LoggerConfiguration` — configure SDK logging: logLevel, callbackLogLevel, logDir, callback on log events.
* `platformConfiguration?: PlatformConfiguration` — set MATTR VII tenant host for remote credential requests.
### New Types & Enums [#new-types--enums]
* **Online presentation (remote/app-to-app):**
* `OnlinePresentationSessionResult` — `{ sessionId, challenge?, mobileCredentialResponse?, error? }`
* `OnlinePresentationResultError` — `{ type: OnlinePresentationResultErrorType, message }`
* `OnlinePresentationResultErrorType` — `SessionAborted | VerificationError | ResponseError | WalletUnavailable | Unknown`
* **Apple Wallet:**
* `AppleWallet` — object with `requestMobileCredentials(challenge): Promise>`
* `RequestMobileCredentialsWithAppleWalletError` / `RequestMobileCredentialsWithAppleWalletErrorType`
* **`requestMobileCredentials` options & errors:**
* `RequestMobileCredentialsOptions` — `{ request, applicationId, walletProviderId?, challenge }`
* `RequestMobileCredentialsErrorType`
* `RequestMobileCredentialsError`
* **`handleDeepLink`:**
* `HandleDeepLinkOptions` — `{ url: string }`
* **`fetchAppleWalletConfiguration`:**
* `FetchAppleWalletConfigurationOptions` — `{ request, applicationId, merchantId }`
* `FetchAppleWalletConfigurationError` / `FetchAppleWalletConfigurationErrorType`
* **`initialize`:**
* `InitializeOptions`, `LoggerConfiguration`, `PlatformConfiguration`, `LogLevel` (`Off | Error | Warn | Info | Debug | Verbose`)
* `InitializeErrorType` = `SdkInitialized | StorageInitializedInBackground`
### New Error Values [#new-error-values]
| Location | New values |
| -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MobileCredentialVerifierErrorType` | `Connectivity`, `StorageInitializedInBackground`, `SdkInitialized`, `PlatformNotSupported`, `PlatformConfigurationInvalid`, `SessionTimedOut`, `SessionAborted`, `DigitalCredentialManager`, `UserCanceled`, `FailedToRequestMobileCredentials`, `AppleWalletNotAvailable` |
| `ProximityPresentationSessionTerminationErrorType` | `Exception` |
## Minimum Requirements [#minimum-requirements]
**iOS**
* iOS 15+ for core SDK functionality.
**Android**
* Android 7 / Nougat / API 24.
* The underlying Android Verifier SDK is built using Kotlin 2.0. This adds some intrinsic dependencies into your build tools.
* Kotlin 2.0 is supported from AGP version [8.5](https://developer.android.com/build/kotlin-support).
* AGP 8.5 is supported from Gradle version [8.7](https://developer.android.com/build/releases/about-agp) and Android Studio Koala [2024.1.1](https://developer.android.com/studio/releases).
## Migration Steps [#migration-steps]
### Update `initialize()` usage [#update-initialize-usage]
The `initialize()` function now accepts an optional options object and returns a `Result`. Update your code to handle the result and possible errors:
```diff
- await initialize();
+ const result = await initialize(options);
+ if (result.isErr()) {
+ // Handle error: result.error
+ }
```
### Update `sendProximityPresentationRequest` calls [#update-sendproximitypresentationrequest-calls]
The `skipStatusCheck` parameter has been renamed to `checkStatus` with inverted semantics. When `checkStatus` is `true` (default), the SDK will verify credential status. When `false`, it will skip status checking. Update all calls accordingly:
```diff
- const response = await sendProximityPresentationRequest({ ..., skipStatusCheck: true });
+ const response = await sendProximityPresentationRequest({ ..., checkStatus: false });
```
| Old Parameter | New Parameter | Mapping |
| ----------------------------------- | ------------------------------ | ------------------ |
| `skipStatusCheck = false` (default) | `checkStatus = true` (default) | No change needed |
| `skipStatusCheck = true` | `checkStatus = false` | Invert the boolean |
### Handle new `ProximityPresentationSessionTerminationErrorType.Exception` case [#handle-new-proximitypresentationsessionterminationerrortypeexception-case]
If you switch over `ProximityPresentationSessionTerminationErrorType`, add handling for the new `Exception` value.
### Update NFC error handling [#update-nfc-error-handling]
The NFC error listener in `registerForNfcDeviceEngagement` now routes parse failures to `onError` instead of rethrowing. Update your error handling logic accordingly.
# Getting started with the Verifier SDKs
URL: /docs/verification/remote-mobile-verifiers/sdks/sdk-getting-started
Description: Set up access to the MATTR Pi mDocs Verifier SDKs, configure SDK tethering, and initialize the SDK in your mobile application.
This guide walks you through the steps required to start building with the MATTR Pi mDocs Verifier
SDKs. By the end, your mobile application will be ready to verify credential presentations. For the
native iOS and Android Verifier SDKs, this includes tethering your application to a MATTR VII
tenant. The React Native Verifier SDK is not tethered: for in-person (proximity) verification it
does not require a MATTR VII tenant or platform configuration, and only needs a tenant for remote
mobile (app-to-app) verification. React Native differences are called out at each step below.
### Request SDK access [#request-sdk-access]
To access the MATTR Pi mDocs Verifier SDKs, complete the
[Get Started form](/docs/resources/get-started) with the following details:
* Your organization name and contact information.
* The platform(s) you plan to build for (iOS, Android, or React Native).
* A brief description of your use case.
### Create a MATTR VII tenant [#create-a-mattr-vii-tenant]
The native iOS and Android Verifier SDKs require a MATTR VII tenant that serves as the backend for
SDK operations including tethering and credential verification. For React Native, a tenant is only
required for remote mobile (app-to-app) verification.
If you are building React Native for in-person verification only, you can skip this step.
1. Log into the [MATTR Portal](https://portal.mattr.global).
2. Select the **Create/switch tenant** button on the top-right side of the screen.\
The *All tenants* panel is displayed, listing any existing tenants.
3. Select the **Create new** button.\
The *New tenant* form is displayed.
4. Use the *Region* dropdown list to select the region your tenant will be hosted in.
5. Use the *Tenant subdomain* text box to insert a subdomain for your tenant (e.g. `in-person-verification`).
6. Select the **Create** button to create the new tenant.
7. Copy the displayed tenant information (`audience`, `auth_url`, `tenant_url`, `client_id` and `client_secret`).
### Create a Verifier Application [#create-a-verifier-application]
The iOS and Android Verifier SDKs are **tethered** to a MATTR VII tenant. On initialization, the SDK registers your app instance with the
tenant and obtains a license, so SDK Tethering must be configured before you initialize the SDK. For
a full explanation of tethering and the capabilities it enables, see [SDK Tethering](/docs/verification/sdks/sdk-tethering).
To tether the SDK, create a Verifier Application on your MATTR VII tenant for the platform you are
building.
Make a request of the following structure to create an iOS Verifier Application configuration on your MATTR VII tenant:
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "My iOS Verifier Application",
"type": "ios",
"bundleId": "com.yourcompany.verifierapp",
"teamId": "YOUR_APPLE_TEAM_ID",
"appAttest": {
"required": false,
"environment": "development"
}
}
```
* `name`: A unique name to identify this Verifier Application.
* `type`: Must be `ios`.
* `bundleId`: The Bundle ID of your iOS app (must match your Xcode project configuration).
* `teamId`: Your Apple Developer Team ID (must match the Team ID used to sign your app).
* `appAttest`: App Attest configuration for the iOS verifier application:
* `required`: When `true`, the app instance must provide a valid App Attest attestation during
registration and token renewal. When `false`, the app can fall back to assertion-only
authentication. See [Attestation vs Assertion](/docs/verification/sdks/sdk-tethering#attestation-vs-assertion-fall-back) for more details.
* `environment`: The App Attest environment (`development` or `production`). Apple recommends
using `development` for testing and `production` for distribution builds.
A successful response returns a `201` status code with the created Verifier Application:
```json title="Response"
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c", // [!code focus]
"name": "My iOS Verifier Application",
"type": "ios",
"bundleId": "com.yourcompany.verifierapp",
"teamId": "YOUR_APPLE_TEAM_ID",
"appAttest": {
"required": false,
"environment": "development"
}
}
```
* `id`: A unique identifier for the Verifier Application (generated by the tenant). You must use this value when initializing the SDK so that it can correctly
identify and authenticate your application.
Make a request of the following structure to create an Android Verifier Application configuration on your MATTR VII tenant:
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "My Android Verifier Application",
"type": "android",
"packageName": "com.yourcompany.verifierapp",
"packageSigningCertificateThumbprints": [
"1232584B6F6A892D356899FB9576C5F226A179E6199F2B7A1D837B5C234C5A8E"
],
"keyAttestation": {
"required": false
},
"openid4vpConfiguration": {
"redirectUri": "com.yourcompany.verifierapp://oid4vp-callback"
}
}
```
* `name`: A unique name to identify this Verifier Application.
* `type`: Must be `android`.
* `packageName`: The package name of your Android application.
* `packageSigningCertificateThumbprints`: SHA-256 hex-encoded fingerprints of the signing key
certificates used to sign your APK or app bundle. This ensures the tenant only accepts requests
from known and trusted applications. Refer to
[Android app signing](/docs/verification/android-app-signing) for more information.
* `keyAttestation`: Key Attestation configuration for the Android verifier application:
* `required`: When `true`, the app instance must provide a valid Key Attestation during
registration and token renewal. When `false`, the app can register and renew tokens using
just an authentication assertion. See [Attestation vs Assertion](/docs/verification/sdks/sdk-tethering#attestation-vs-assertion-fall-back) for more details.
* `openid4vpConfiguration.redirectUri`: Required by the create-application endpoint, which needs at
least one of `openid4vpConfiguration` or `dcApiConfiguration`. In-person proximity verification
does not use this redirect, so any valid custom-scheme URI is accepted here.
A successful response returns a `201` status code with the created Verifier Application:
```json title="Response"
{
"id": "a82bfa46-72a0-4cde-b6cb-2a0de7e2f3c4", // [!code focus]
"name": "My Android Verifier Application",
"type": "android",
"packageName": "com.yourcompany.verifierapp",
"packageSigningCertificateThumbprints": [
"1232584B6F6A892D356899FB9576C5F226A179E6199F2B7A1D837B5C234C5A8E"
],
"keyAttestation": {
"required": false
}
}
```
* `id`: A unique identifier for the Verifier Application (generated by the tenant). You must use this value when initializing the SDK so that it can correctly
identify and authenticate your application.
SDK Tethering is currently not required for the React Native Verifier SDK.
### Initialize the SDK [#initialize-the-sdk]
When you initialize the SDK, you must provide a `PlatformConfiguration` object with your tenant host and the `id` of the Verifier Application you created. This allows the SDK to register the app instance with your tenant and obtain a license to operate.
Initialize the SDK with your platform configuration. The `initialize` method is asynchronous, so call it from an asynchronous context:
```swift title="Initialization"
let platformConfig = PlatformConfiguration(
tenantHost: URL(string: "https://your-tenant.vii.mattr.global")!,
applicationId: "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
)
try await MobileCredentialVerifier.shared.initialize(
platformConfiguration: platformConfig
)
```
* `tenantHost`: The URL of your MATTR VII tenant. This must be the tenant where your iOS Verifier
Application is configured.
* `applicationId`: The `id` of your configured iOS Verifier Application.
Initialize the SDK with your platform configuration:
```kotlin title="Initialization"
val platformConfig = PlatformConfiguration(
tenantHost = URL("https://your-tenant.vii.mattr.global"),
applicationId = "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
)
MobileCredentialVerifier.initialize(context, platformConfig)
```
* `tenantHost`: The URL of your MATTR VII tenant where your Android Verifier Application is configured.
* `applicationId`: The `id` of your configured Android Verifier Application.
The React Native Verifier SDK is **not** tethered, so initialization does not register an app
instance or require a `platformConfiguration` object.
## Next steps [#next-steps]
Your application is now initialized and tethered to your MATTR VII tenant, ready to verify
credentials. Explore the following resources to start building:
* In-person verification:
* [Quickstart](/docs/verification/in-person-quickstart): Run a sample in-person verifier app end-to-end.
* [Tutorial](/docs/verification/in-person-tutorial): Detailed walkthrough of building an app that can verify credentials in-person using Bluetooth proximity presentations.
* Remote mobile verification:
* [Quickstart](/docs/verification/remote-mobile-verifiers/quickstart): Run a sample remote mobile verifier app end-to-end.
* [Tutorial](/docs/verification/remote-mobile-verifiers/tutorial): Detailed walkthrough of building an app that can request and verify credentials from a wallet app on the same device (app-to-app).
# Configure SDK logging
URL: /docs/verification/remote-mobile-verifiers/sdks/sdk-logging
Description: Learn how to configure logging in the MATTR Verifier SDKs, including log levels, callback handlers, and accessing log files across iOS, Android, and React Native platforms.
The MATTR Verifier SDKs include a built-in logging system that records internal SDK operations. This
is useful for debugging integration issues, monitoring SDK behavior, and capturing diagnostic
information during development and testing.
By default, SDK logs are stored on the device. The SDK itself does not transmit logs to any
external service, although your application can choose to forward log events elsewhere if you
register a callback.
## What information the SDK can log [#what-information-the-sdk-can-log]
The SDK can log information about its internal operations, including errors and warnings encountered during SDK operations.
All log entries include the log level and a descriptive message. This information helps you
diagnose issues and understand how the SDK operates within your application.
## Log levels [#log-levels]
The SDK supports the following log levels, ordered from most to least verbose:
| Level | Description |
| --------- | -------------------------------------------------------------------------- |
| `Verbose` | Fine-grained informational events, most detailed output. |
| `Debug` | Detailed information useful during development. |
| `Info` | General informational messages about SDK operations. |
| `Warning` | Potentially harmful situations or unexpected behavior (`Warn` in Android). |
| `Error` | Error events that might still allow the SDK to continue running. |
| `Assert` | Severe error events that indicate a critical failure (Android only). |
| `Off` | Disables logging entirely. |
The SDK uses the configured log level as a threshold: it records log events at the specified level
and any less verbose levels. For example, setting the level to `Info` captures `Info`, `Warning`,
`Error`, and `Assert` events, but not `Debug` or `Verbose`.
## Configure logging at initialization [#configure-logging-at-initialization]
You can configure logging behavior by passing a `loggerConfiguration` object to the SDK's
`initialize` method. This configuration accepts two separate log levels:
* **`logLevel`**: Controls which log events are written to the log file.
* **`callbackLogLevel`**: Controls which log events trigger the optional callback function.
```swift title="Configure logging during initialization"
try mobileCredentialVerifier.initialize(
loggerConfiguration: LoggerConfiguration( // [!code highlight]
logLevel: .Info, // [!code highlight]
callbackLogLevel: .Warning // [!code highlight]
) // [!code highlight]
)
```
* `logLevel`: Sets the minimum level for writing log entries to the log file. Defaults to `.Off`.
* `callbackLogLevel`: Sets the minimum level for invoking the callback closure. Defaults to `.Off`.
Refer to the
[`LoggerConfiguration`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/loggerconfiguration)
reference documentation for additional details.
```kotlin title="Configure logging during initialization"
mobileCredentialVerifier.initialize(
loggerConfiguration = Logger.LoggerConfiguration(
logLevel = Logger.LogLevel.INFO,
callbackLogLevel = Logger.LogLevel.WARN
),
// ...
)
```
* `logLevel`: Sets the minimum level for writing log entries to the log file and Logcat. Defaults to `Logger.LogLevel.OFF`.
* `callbackLogLevel`: Sets the minimum level for invoking the callback function. Defaults to `Logger.LogLevel.OFF`.
* `logDir`: Optional. Specifies a local directory to store log files. If not provided, logs won't be stored to file.
Refer to the
[`Logger.LoggerConfiguration`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/m-docs%20-verifier%20-s-d-k/global.mattr.mobilecredential.verifier.util/-logger/-logger-configuration/index.html)
reference documentation for additional details.
```ts title="Configure logging during initialization"
import { LogLevel } from "@mattrglobal/mobile-credential-verifier-react-native"
const initializeResult = await mobileCredentialVerifier.initialize({
loggerConfiguration: { // [!code highlight]
logLevel: LogLevel.Info, // [!code highlight]
callbackLogLevel: LogLevel.Warn, // [!code highlight]
}, // [!code highlight]
})
if (initializeResult.isErr()) {
const { error } = initializeResult
// handle error scenarios
return
}
```
* `logLevel`: Sets the minimum level for writing log entries to the log file and console. Defaults to `LogLevel.Off`.
* `callbackLogLevel`: Sets the minimum level for invoking the callback function. Defaults to `LogLevel.Off`.
* `logDir`: Optional. Specifies a directory to store log files. On iOS, a default directory is used. On Android, logs won't be stored to file if not provided.
Refer to the
[`LoggerConfiguration`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/types/LoggerConfiguration.html)
reference documentation for additional details.
## Handle log events with a callback [#handle-log-events-with-a-callback]
You can register a callback function during initialization to receive log events in real time. This
allows your application to process log events as they occur, for example to forward them to a custom
logging service, display them in a debug console, or filter specific events for monitoring.
The callback is only invoked for log events at or above the `callbackLogLevel` threshold.
```swift title="Register a logging callback"
try mobileCredentialVerifier.initialize(
loggerConfiguration: LoggerConfiguration(
logLevel: .Info,
callbackLogLevel: .Warning,
callback: { logEvent in // [!code highlight]
print("[\(logEvent.level)] \(logEvent.message)") // [!code highlight]
} // [!code highlight]
)
)
```
The callback receives a
[`LogEvent`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/logevent)
object with the following properties:
* `level`: The [`LogLevel`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/loglevel) of the event.
* `message`: A string describing the log event.
```kotlin title="Register a logging callback"
mobileCredentialVerifier.initialize(
loggerConfiguration = Logger.LoggerConfiguration(
logLevel = Logger.LogLevel.INFO,
callbackLogLevel = Logger.LogLevel.WARN,
callback = { priority, tag, message, throwable -> // [!code highlight]
Log.d("VerifierSDK", "[$tag] $message") // [!code highlight]
} // [!code highlight]
),
// ...
)
```
The callback function receives the following parameters:
* `priority`: An integer representing the log priority level.
* `tag`: An optional string tag identifying the log source.
* `message`: A string describing the log event.
* `throwable`: An optional `Throwable` associated with the log event (for error-level logs).
```ts title="Register a logging callback"
import { LogLevel } from "@mattrglobal/mobile-credential-verifier-react-native"
const initializeResult = await mobileCredentialVerifier.initialize({
loggerConfiguration: {
logLevel: LogLevel.Info,
callbackLogLevel: LogLevel.Warn,
callback: (log) => { // [!code highlight]
console.log(`[${log.logLevel}] ${log.tag ?? ""}: ${log.message ?? ""}`) // [!code highlight]
}, // [!code highlight]
},
})
if (initializeResult.isErr()) {
const { error } = initializeResult
// handle error scenarios
return
}
```
The callback receives a log object with the following properties:
* `logLevel`: The [`LogLevel`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/enums/LogLevel.html) of the event.
* `message`: An optional string describing the log event.
* `tag`: An optional string tag identifying the log source.
## Access the log file [#access-the-log-file]
The SDK writes log entries to a file that you can access for debugging and diagnostics. The log file
contains entries from the previous two calendar days.
```swift title="Get the log file path"
let logFilePath = mobileCredentialVerifier.getCurrentLogFilePath()
```
The
[`getCurrentLogFilePath`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-ios/latest/documentation/mobilecredentialverifiersdk/mobilecredentialverifier/getcurrentlogfilepath\(\))
method returns the file path as a string, or `nil` if no log file is available.
To read the logs, use the returned file path to load the file contents into `Data`, then decode the
data into text and split it into individual log messages as needed.
```kotlin title="Get the log file path"
val logFilePath = mobileCredentialVerifier.getLog()
```
The
[`getLog`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-android/latest/m-docs%20-verifier%20-s-d-k/global.mattr.mobilecredential.verifier/-mobile-credential-verifier/get-log.html)
method returns the full path of the log file (with a `.log` extension), or `null` if no log file is
available.
The file contains log entries from the previous two calendar days.
```ts title="Get the log file path"
const logFilePath = await mobileCredentialVerifier.getCurrentLogFilePath()
```
The
[`getCurrentLogFilePath`](https://api-reference-sdk.mattr.global/mobile-credential-verifier-react-native/latest/functions/getCurrentLogFilePath.html)
method returns a `Promise` resolving to the log file path string, or `null` if no log file is
available.
# SDK Tethering
URL: /docs/verification/remote-mobile-verifiers/sdks/sdk-tethering
Description: Learn how MATTR Verifier SDKs are tethered to a MATTR VII tenant through Verifier Application configuration, enabling operational insights and licensing.
SDK Tethering ties each SDK/app instance to a MATTR VII tenant. Tethering establishes a trust
relationship between your mobile application and the MATTR VII tenant, enabling the following
capabilities:
* **Operational insights**: View details about registered and active app instances directly from
your tenant.
* **Licensing**: On first initialization, the SDK registers the app instance with your tenant and
obtains a license. The majority of the SDK's APIs require a valid license to operate.
* **Remote management channel**: SDK Tethering establishes a channel that we expect to extend in
the future with capabilities such as remote syncing of trusted issuer lists and eventing.
SDK Tethering is required from the following SDK versions:
* **iOS Verifier SDK**: 6.0.0
* **Android Verifier SDK**: 7.0.0
## How it works [#how-it-works]
The tethering process involves three steps:
1. **Configure a Verifier Application on your MATTR VII tenant**: You register your mobile app by
creating a Verifier Application, identified by the bundle identifier and team ID (iOS) or the package
fingerprint (Android).
2. **Initialize the SDK with your tenant details**: When you initialize the SDK in your app, you
pass the details of the MATTR VII tenant and the Verifier Application you configured on it.
3. **Automatic communication**: Once initialized, instances of your app will automatically
communicate with the configured MATTR VII tenant and retrieve the required tokens to operate
and make requests to the tenant when required.
## Token validity and offline use [#token-validity-and-offline-use]
The tokens issued during this process have configurable validity periods controlled by the
`maxTimeOfflineInSecs` field on your Verifier Application configuration. This means your app can
function without internet connectivity to meet different use cases:
* **Minimum**: 1 day (86400 seconds)
* **Maximum**: 30 days (2592000 seconds)
* **Default**: 7 days (604800 seconds)
When the license token expires, the SDK must reconnect to the MATTR VII tenant to renew it. Network
access is required when registration or renewal is performed.
## Configuring SDK Tethering [#configuring-sdk-tethering]
### Configure Verifier Applications [#configure-verifier-applications]
Make a request of the following structure to create an iOS Verifier Application configuration on your MATTR VII tenant:
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "My iOS Verifier Application",
"type": "ios",
"bundleId": "com.yourcompany.verifierapp",
"teamId": "YOUR_APPLE_TEAM_ID",
"appAttest": {
"required": false,
"environment": "development"
}
}
```
* `name`: A unique name to identify this Verifier Application.
* `type`: Must be `ios`.
* `bundleId`: The Bundle ID of your iOS app (must match your Xcode project configuration).
* `teamId`: Your Apple Developer Team ID (must match the Team ID used to sign your app).
* `appAttest`: App Attest configuration for the iOS verifier application:
* `required`: When `true`, the app instance must provide a valid App Attest attestation during
registration and token renewal. When `false`, the app can fall back to assertion-only
authentication. See [Attestation vs Assertion](/docs/verification/sdks/sdk-tethering#attestation-vs-assertion-fall-back) for more details.
* `environment`: The App Attest environment (`development` or `production`). Apple recommends
using `development` for testing and `production` for distribution builds.
A successful response returns a `201` status code with the created Verifier Application:
```json title="Response"
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c", // [!code focus]
"name": "My iOS Verifier Application",
"type": "ios",
"bundleId": "com.yourcompany.verifierapp",
"teamId": "YOUR_APPLE_TEAM_ID",
"appAttest": {
"required": false,
"environment": "development"
}
}
```
* `id`: A unique identifier for the Verifier Application (generated by the tenant). You must use this value when initializing the SDK so that it can correctly
identify and authenticate your application.
Make a request of the following structure to create an Android Verifier Application configuration on your MATTR VII tenant:
```http title="Request"
POST /v2/presentations/applications
```
```json title="Request body"
{
"name": "My Android Verifier Application",
"type": "android",
"packageName": "com.yourcompany.verifierapp",
"packageSigningCertificateThumbprints": [
"1232584B6F6A892D356899FB9576C5F226A179E6199F2B7A1D837B5C234C5A8E"
],
"keyAttestation": {
"required": false
},
"openid4vpConfiguration": {
"redirectUri": "com.yourcompany.verifierapp://oid4vp-callback"
}
}
```
* `name`: A unique name to identify this Verifier Application.
* `type`: Must be `android`.
* `packageName`: The package name of your Android application.
* `packageSigningCertificateThumbprints`: SHA-256 hex-encoded fingerprints of the signing key
certificates used to sign your APK or app bundle. This ensures the tenant only accepts requests
from known and trusted applications. Refer to
[Android app signing](/docs/verification/android-app-signing) for more information.
* `keyAttestation`: Key Attestation configuration for the Android verifier application:
* `required`: When `true`, the app instance must provide a valid Key Attestation during
registration and token renewal. When `false`, the app can register and renew tokens using
just an authentication assertion. See [Attestation vs Assertion](/docs/verification/sdks/sdk-tethering#attestation-vs-assertion-fall-back) for more details.
* `openid4vpConfiguration.redirectUri`: Required by the create-application endpoint, which needs at
least one of `openid4vpConfiguration` or `dcApiConfiguration`. In-person proximity verification
does not use this redirect, so any valid custom-scheme URI is accepted here.
A successful response returns a `201` status code with the created Verifier Application:
```json title="Response"
{
"id": "a82bfa46-72a0-4cde-b6cb-2a0de7e2f3c4", // [!code focus]
"name": "My Android Verifier Application",
"type": "android",
"packageName": "com.yourcompany.verifierapp",
"packageSigningCertificateThumbprints": [
"1232584B6F6A892D356899FB9576C5F226A179E6199F2B7A1D837B5C234C5A8E"
],
"keyAttestation": {
"required": false
}
}
```
* `id`: A unique identifier for the Verifier Application (generated by the tenant). You must use this value when initializing the SDK so that it can correctly
identify and authenticate your application.
SDK Tethering is currently not required for the React Native Verifier SDK.
### Initialize the SDK with platform configuration [#initialize-the-sdk-with-platform-configuration]
When you initialize the SDK, you must provide a `PlatformConfiguration` object with your tenant host and the `id` of the Verifier Application you created. This allows the SDK to register the app instance with your tenant and obtain a license to operate.
Initialize the SDK with your platform configuration. The `initialize` method is asynchronous, so call it from an asynchronous context:
```swift title="Initialization"
let platformConfig = PlatformConfiguration(
tenantHost: URL(string: "https://your-tenant.vii.mattr.global")!,
applicationId: "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
)
try await MobileCredentialVerifier.shared.initialize(
platformConfiguration: platformConfig
)
```
* `tenantHost`: The URL of your MATTR VII tenant. This must be the tenant where your iOS Verifier
Application is configured.
* `applicationId`: The `id` of your configured iOS Verifier Application.
Initialize the SDK with your platform configuration:
```kotlin title="Initialization"
val platformConfig = PlatformConfiguration(
tenantHost = URL("https://your-tenant.vii.mattr.global"),
applicationId = "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
)
MobileCredentialVerifier.initialize(context, platformConfig)
```
* `tenantHost`: The URL of your MATTR VII tenant where your Android Verifier Application is configured.
* `applicationId`: The `id` of your configured Android Verifier Application.
SDK Tethering is currently not required for the React Native Verifier SDK.
Once your Verifier Application configurations are created, your application will be able to use the SDK and
interact with the MATTR VII platform (for example, to verify credential presentations).
## Managing application instances [#managing-application-instances]
Once your Verifier Application is configured and the SDK is initialized, each device that launches
your app registers as a new application instance on your tethered MATTR VII tenant. You can view and manage
these instances via the MATTR VII API.
### Retrieve all registered instances [#retrieve-all-registered-instances]
To view all registered instances for a Verifier Application and track usage:
```http title="Request"
GET /v2/presentations/applications/{applicationId}/instances
```
* `applicationId` : The `id` of the Verifier Application you want to inspect.
The response includes a paginated list of all registered instances:
```json title="Response"
{
"data": [
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c",
"appAttestationType": "app_attestation",
"registeredAt": "2023-10-05T14:48:00.000Z",
"licenseExpiresAt": "2024-10-05T14:48:00.000Z",
"lastAttestedAt": "2023-12-01T10:30:00.000Z",
"externalReferenceId": "external-ref-12345",
"deviceDetails": {
"deviceModel": "iPhone 12",
"deviceMake": "Apple",
"osVersion": "iOS 14.4"
},
"sdkDetails": {
"sdkVersion": "1.2.3"
}
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
Each instance includes:
* `id` : Unique identifier for the registered instance.
* `appAttestationType` : The type of attestation used during registration (`none`,
`app_attestation`, or `key_attestation`).
* `registeredAt` : When the instance was first registered.
* `licenseExpiresAt` : When the instance's license expires (the Verifier SDK will automatically handle license renewal).
* `lastAttestedAt` : When the instance was last attested.
* `deviceDetails` : Information about the device (model, make, OS version).
* `sdkDetails` : Information about the SDK version used by the instance.
This is useful for tracking how many devices are actively using your application and monitoring usage quotas.
### Delete a specific instance [#delete-a-specific-instance]
To remove a specific registered instance:
```http title="Request"
DELETE /v2/presentations/applications/{applicationId}/instances/{instanceId}
```
* `applicationId` : The `id` of the Verifier Application.
* `instanceId` : The `id` of the specific instance to delete.
Once deleted, the instance can no longer interact with the platform or receive tokens, and any
existing tokens are revoked.
Deleting instances is primarily useful during **testing** when you have a limited number of devices
and need to re-register a fresh instance (for example, to test the initial registration flow again).
In production, there is nothing preventing the application from requesting another token on the next
launch, which would create a new instance — so deleting instances is not an effective way to block
a device.
## Attestation vs Assertion fall-back [#attestation-vs-assertion-fall-back]
When configuring a Verifier Application, you control whether your MATTR VII tenant requires
**attestation** (hardware-backed proof of app integrity) or also accepts a lighter-weight
**assertion** (a cryptographic signature proving key possession) during instance registration and
token renewal.
Each platform has an attestation configuration with a `required` boolean:
* When `required` is `true`, the app instance must provide a valid attestation during registration
and token renewal.
* When `required` is `false`, your tenant also accepts an assertion when an attestation is not
available.
The SDK handles this automatically. It always attempts to provide an attestation, and falls back to
an assertion if it cannot generate one (for example, when the platform attestation service is
temporarily unavailable). Your tenant then accepts or rejects the request based on the `required`
setting. Your application does not need to manage attestation or assertion details directly.
### When to use each setting [#when-to-use-each-setting]
| Scenario | Recommended setting |
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| **Production apps in distribution** | `required: true`: Provides the strongest integrity guarantees by verifying the app and device through OS-level attestation. |
| **Development and testing** | `required: false`: Useful when running on simulators or devices where attestation services are unavailable. |
| **Broad device compatibility** | `required: false`: Some older devices may not support hardware attestation. The assertion fall-back ensures these devices can still register. |
Setting attestation to `required: false` reduces the security guarantees of the tethering
process. Only use this setting when you have a specific need, such as supporting older devices
or during development.
# Build a web application that can request and verify credentials via the Digital Credentials API
URL: /docs/verification/remote-web-verifiers/dc-api/guide
Description: Learn how to use the Verifier Web SDK to verify credentials using the Digital Credentials API (DC API) in a web application.
## Overview [#overview]
DC API support is currently offered as a tech preview. The Digital Credentials API specification
itself is still under active development in the W3C Web Incubator CG, and platform implementations
continue to evolve. As such, functionality may be limited, may not work in all scenarios, and could
change or break without prior notice as browsers and operating systems update their implementations.
This guide demonstrates how to use the
[Verifier Web SDK](/docs/verification/remote-web-verifiers/sdks/overview) to verify credentials using
the [Digital Credentials API](/docs/verification/remote-web-verifiers/dc-api/overview) (DC API).
## Prerequisites [#prerequisites]
This guide builds on the [Verifier Web SDK tutorial](/docs/verification/remote-web-verifiers/tutorial).
It is recommended to complete that tutorial first, then return here to add support for the [DC API workflow](/docs/verification/remote-web-verifiers/workflow#dc-api-workflow).
You will also need:
* Version `2.1.0` or later of the
[Verifier Web SDK](https://www.npmjs.com/package/@mattrglobal/verifier-sdk-web).
* A web browser that supports the Digital Credentials API:
* Chrome or a Chromium based browser v138 or later.
* Safari v26 or later.
* A wallet application that supports the Digital Credentials API for testing:
* Google Wallet with a compliant credential, or Google's developer wallet with a test credential.
* Apple Wallet (requires iOS 26 on an iPhone 11 or later) with a compliant credential or a [Wallet Identity Developer profile](https://developer.apple.com/bug-reporting/profiles-and-logs/?name=wallet\&platform=ios) installed.
* MATTR Labs wallet, available to selected MATTR customers/partners.
## Adjusting your web application to use the DC API [#adjusting-your-web-application-to-use-the-dc-api]
Most of the Verifier Web SDK integration remains the same when using the DC API. The Verifier Web SDK automatically uses the DC API when **all** of the following conditions are met:
1. The MATTR VII verifier application configuration has DC API support enabled.
2. The user's browser supports the DC API.
3. The credential request is for a **single credential** (DC API doesn't support multiple credentials
in one request).
4. No `walletProviderId` is provided in the request options.
If any condition is not met, the SDK falls back to the standard OID4VP flow.
These are the adjustments needed to make sure your verifier web application meets these conditions:
### Add DC API support to your verifier application configuration [#add-dc-api-support-to-your-verifier-application-configuration]
1. Expand the **Credential Verification** section in the left-hand navigation panel.
2. Select **Applications**.
3. Click on the verifier application you want to update.
4. Use the *Supported protocols* checkbox to select **Digital Credentials API (DC API)**.
5. In the *DC API configuration* section that appears, use the checkboxes to specify which platforms should use DC API (at least one must be selected):
* Select **Mobile** to enable DC API on mobile browsers.
* Select **Desktop** to enable DC API on desktop browsers.
6. Select **Update** to save your changes.
Make the following request to your MATTR VII tenant to add the `dcApiConfiguration` block to your [verifier application configuration](/docs/verification/remote-verification-api-reference/verifier-applications#update-a-verifier-application):
```http title="Request"
PUT /v2/presentations/applications/{applicationId}
```
* `applicationId`: The ID of the verifier application you want to update.
```json title="Request body"
{
// ... your existing configuration
"dcApiConfiguration": {
"supportedBrowserPlatforms": {
"mobile": true,
"desktop": true
}
}
}
```
* `supportedBrowserPlatforms`: Specify which platforms should use DC API:
* `mobile`: Set to `true` to enable DC API on mobile browsers.
* `desktop`: Set to `true` to enable DC API on desktop browsers.
### Conditionally omit the walletProviderId when calling the `requestCredentials` method [#conditionally-omit-the-walletproviderid-when-calling-the-requestcredentials-method]
When requesting credentials via the DC API, the `walletProviderId` must be omitted (set to `undefined`) to allow
the SDK to use the DC API flow.
You can achieve this by using the SDK's `isDigitalCredentialsApiSupported` method to check if the user's browser supports DC API
and adjusting the request options accordingly (This method returns `true` if the browser supports DC API, `false` otherwise):
```typescript title="Request credentials with DC API"
const options: MATTRVerifierSDK.RequestCredentialsOptions = {
// The array must contain exactly one query to align with the DC API's single credential requirement.
credentialQuery: [credentialQuery],
challenge: MATTRVerifierSDK.utils.generateChallenge(),
openid4vpConfiguration: {
redirectUri: window.location.origin,
walletProviderId: MATTRVerifierSDK.isDigitalCredentialsApiSupported() ? undefined : "your-wallet-provider-id",
},
};
const results = await MATTRVerifierSDK.requestCredentials(options);
```
## Accepting credentials from an Apple Wallet via DC API [#accepting-credentials-from-an-apple-wallet-via-dc-api]
When accepting credentials from Apple Wallets via the DC API, trust is anchored in an external root CA certificate issued by Apple Business Connect. You must manually create a verification request signer in MATTR VII and link it to the Apple-issued certificate. See [external certificates](/docs/concepts/chain-of-trust#external-certificates) for background.
This involves the following steps:
1. [Setup an Apple Business Connect account](#setup-an-apple-business-connect-account).
2. [Create a verification request signer on your MATTR VII tenant](#create-a-mattr-vii-verification-request-signer).
3. [Create a matching Apple Business Connect certificate](#create-an-apple-business-connect-certificate).
4. [Activate the verification request signer](#activate-the-verification-request-signer).
### Setup an Apple Business Connect account [#setup-an-apple-business-connect-account]
Set up an Apple Business Account for your company, and register with Apple. See [Apple Business Connect User Guide](https://support.apple.com/guide/apple-business-connect/welcome/web) for more details.
### Create a Certificate Signing Request (CSR) [#create-a-certificate-signing-request-csr]
Next you will use MATTR VII to create a Certificate Signing Request (CSR). This will be shared with Apple Business Connect to create a matching certificate.
Currently this action can only be performed via an API request.
Make the following request to your MATTR VII tenant to
[create a verification request signer](/docs/api-reference/platform/verification-request-signers/createVerificationRequestSigner):
```http title="Request"
POST /v2/presentations/certificates/verifier-signers
```
```json title="Request body"
{
"emailAddress": "user@example.com",
"country": "US",
"stateOrProvinceName": "AL",
"commonName": "my-verifier.example.com",
"organizationName": "MATTR Learn",
"caType": "apple"
}
```
* `emailAddress`: The email address of the domain (or IT) administrator.
* `country`: The two-letter country code (ISO 3166-1 alpha-2) representing your company's location.
* `stateOrProvinceName`: The company's officially recognized state, province, region, or locale.
* `commonName`: Fully qualified domain name (FQDN) where the verifier application is hosted.
* `organizationName`: The official name of your company.
* `caType`: Set to `apple` to indicate that this signer is for Apple Wallet verification requests.
*Response*
A successful `201` response indicates that the verification request signer was created
successfully:
```json title="Response body"
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1", // [!code focus]
"csrPem": "-----BEGIN CERTIFICATE REQUEST-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE REQUEST-----", // [!code focus]
"active": false,
"caType": "apple"
}
```
Make note of the following values:
* `id`: The verification request signer ID. You will use it later to activate the signer after creating the matching Apple Business Connect certificate.
* `csrPem`: You will need it in the next step to create a matching Apple Business Connect certificate in your Apple Developer account.
### Create an Apple Business Connect certificate [#create-an-apple-business-connect-certificate]
Log into the [Apple Business Connect](https://businessconnect.apple.com/) portal and create a new Apple
Business Connect certificate using the CSR you obtained in the previous step. For detailed
instructions, see [Apple's documentation](https://support.apple.com/en-gb/guide/apple-business-connect/abcbffe722af/1.0/web/1.0).
Once you create the certificate in Apple Business Connect, download the certificate file in `pem` format. You will use it in the next step to activate the verification request signer in your MATTR VII verifier tenant.
### Activate the verification request signer [#activate-the-verification-request-signer]
To activate the verification request signer you created earlier, make the following request to your MATTR VII tenant to
[update the verification request signer](/docs/api-reference/platform/verification-request-signers/updateVerificationRequestSigner) and activate it:
```http title="Request"
PUT /v2/presentations/certificates/verifier-signers/{verifierSignerId}
```
* `verifierSignerId`: The ID of the verification request signer you created earlier.
```json title="Request body"
{
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----"
}
```
* `active`: Set to `true` to activate the signer.
* `certificatePem`: The Apple Business Connect certificate you created in the previous step, in `pem` format.
Once the verification request signer is activated, requests coming from your MATTR VII verifier tenant can be validated, as they are signed using the private key associated with this certificate.
## Test the DC API workflow [#test-the-dc-api-workflow]
To test DC API integration, you need:
1. A supported browser (see compatibility above).
2. A DC API-compatible wallet with matching credentials:
* **Google Developer Wallet**: Available on supported Android devices.
* **Apple Wallet**: Available on iOS 26 on an iPhone 11 or later, with either a valid credential or a [Wallet Identity Developer profile](https://developer.apple.com/bug-reporting/profiles-and-logs/?name=wallet\&platform=ios) installed.
* **MATTR Labs Wallet**: [Contact us](mailto:dev-support@mattr.global) to get access.
The user experience should be seamless—the wallet interface appears directly in the browser without
leaving your application.
## Important considerations [#important-considerations]
* **Apple Wallet limitations**: To accept credentials from Apple Wallets in production environments, your verifier application must be registered and approved by Apple. For more information, see [Apple's documentation](https://support.apple.com/en-gb/guide/apple-business-connect/abcbffe722af/1.0/web/1.0).
* **No pre-flight check**: There's no way to check in advance if the user has a matching credential in a DC API-compatible wallet. If they don't, the wallet will show a "No matching credentials found" message.
* **Error handling**: Always implement robust error handling for scenarios where the user lacks compatible credentials or denies consent.
# Remote web verification DC API journey pattern
URL: /docs/verification/remote-web-verifiers/dc-api/journey-pattern
This journey pattern is used to verify an mDoc remotely via an
[online verification workflow](/docs/verification/remote-overview), as per
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) using the [DC API](/docs/verification/remote-web-verifiers/dc-api/overview).
## Overview [#overview]
* **Issuance channel**: Remote, unsupervised
* **Device/s**: Same-device / Cross-device
* **Formats**: mDocs
* **Information assurance level**: High
* **Identity assurance level**: High
## Journey flow [#journey-flow]
## Architecture [#architecture]
### Interacting with the verifier application [#interacting-with-the-verifier-application]
The user accesses a verifier web application using a supported web browser, on either a desktop or a mobile device.
### Requesting a credential for verification [#requesting-a-credential-for-verification]
Within the verifier application, the user initiates an interaction that requires presenting a mobile document (mDoc) for verification.
The verifier application embeds the MATTR Verifier Web SDK and first checks whether the user’s browser supports the Digital Credentials API (DC API). If supported, the verifier application uses the SDK to initiate a presentation session with a configured MATTR VII verifier tenant.
The request sent to the MATTR VII verifier tenant specifies:
* Which credentials are required
* Which claims from those credentials are needed for verification
The MATTR VII verifier tenant creates a new presentation session and returns a verification request object to the verifier application.
### Invoking the Digital Credentials API [#invoking-the-digital-credentials-api]
The verifier application passes the verification request object to the browser to invoke the Digital Credentials API.
Based on the user’s device and environment, the browser presents an appropriate verification interface to the user:
* On desktop devices, this may be rendered as a QR code
* On mobile devices, this may be rendered as an in-browser control (for example, a button) to start the verification directly
The user initiates the verification process by scanning the QR code or interacting with the in-browser control.
### Selecting and reviewing a credential [#selecting-and-reviewing-a-credential]
Once initiated, the browser forwards the verification request to the user’s mobile device.
The mobile operating system displays a system-managed interface listing matching credentials available from installed wallet applications that are registered to handle DC API requests. This interface is rendered by the mobile device and appears on top of the web browser.
The user selects a credential to present.
* On some platforms (for example, iOS), if only one compatible wallet holds a matching credential, the operating system may directly open that wallet without showing a selection interface.
### Invoking the wallet application [#invoking-the-wallet-application]
The wallet application authenticates the user and retrieves the verification request details, including:
* Which credential is being requested
* Which claims are required
* The relying party requesting the information
The wallet application displays the credential details to the user for review. This interface is rendered by the wallet application and is displayed on top of the web browser.
The user reviews the request and, if comfortable, provides consent to share the credential.
### Verifying the credential [#verifying-the-credential]
After consent is given, the wallet application returns an encrypted presentation response to the browser.
The browser forwards this presentation response to the verifier application, which then submits it to the MATTR VII verifier tenant.
The MATTR VII verifier tenant decrypts and verifies the presentation, performing checks to ensure:
* The credential has not been tampered with
* The credential has not been revoked or suspended
* The credential has not expired
* The credential was issued by a trusted issuer
### Displaying verification results [#displaying-verification-results]
The MATTR VII verifier tenant returns the verification results to the verifier application.
The verifier application displays the results to the user, allowing them to continue their interaction based on the outcome of the verification.
The MATTR VII verifier tenant can also be configured to return the verification result to a secure back-end service instead of the front-end, depending on implementation needs.
# Digital Credentials API
URL: /docs/verification/remote-web-verifiers/dc-api/overview
Description: Learn what the W3C Digital Credentials API (DC API) is, how it streamlines digital credential verification on the web, and how MATTR VII verifier capabilities support it alongside OpenID4VP and ISO/IEC 18013-7 to deliver smoother, more privacy-preserving online verification.
DC API support is currently offered as a tech preview. The Digital Credentials API specification
itself is still under active development in the W3C Web Incubator CG, and platform implementations
continue to evolve. As such, functionality may be limited, may not work in all scenarios, and could
change or break without prior notice as browsers and operating systems update their implementations.
## What is the Digital Credentials API? [#what-is-the-digital-credentials-api]
The **Digital Credentials API (DC API)** is a web browser standard that lets websites request
digital credentials, and digital wallets respond, directly through the browser.
Instead of redirecting a user from a website to a separate wallet app and back, the browser
itself orchestrates the exchange. The user sees a single, consistent prompt and picks the
credential that satisfies the request, regardless of which wallet on the device stores it.
The DC API is being incubated in the
[W3C Web Incubator Community Group](https://wicg.github.io/digital-credentials/) and is
incorporated into the [ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html)
technical specification for remote verification of mDocs.
This page is written from the **verifier** perspective. For the holder perspective, see the
[holder DC API overview](/docs/holding/dc-api/overview).
## Why the Digital Credentials API matters for verifiers [#why-the-digital-credentials-api-matters-for-verifiers]
Until recently, online credential verification largely relied on redirect-based flows. A
website would invoke a wallet via a custom URL scheme, hand off the user, and wait for the
wallet to redirect back with a response. That model works, but it creates real friction:
* Users are presented with a long list of possible wallet apps and have to guess which
one holds the right credential.
* Many users land in a dead-end, having picked a wallet that is not installed or does not
hold the credential the verifier is asking for.
* Verifiers end up restricting the wallets they support to keep the experience manageable,
which limits user choice and slows ecosystem growth.
The DC API addresses these issues at the browser and operating system level. Wallets
register the credentials they hold with the operating system. When a website asks for a
credential, the OS finds matching credentials across every registered wallet and shows
the user a single, unified picker. The focus moves from **choosing a wallet** to
**choosing the right credential**.
For verifiers, this translates into higher completion rates, fewer abandoned journeys, and
broader reach. A single integration can serve users regardless of which wallet they happen
to use, including wallets built into the operating system itself.
## Where the DC API fits alongside OpenID4VP and ISO/IEC 18013-7 [#where-the-dc-api-fits-alongside-openid4vp-and-isoiec-18013-7]
The DC API is a transport, not a replacement for the existing credential exchange
protocols. It defines how a verification request and response move between a website and a
wallet through the browser. The actual request-and-response messages still follow
established standards.
| Standard / specification | What it defines | Relationship to the DC API |
| ------------------------------------------------------------------ | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| [OpenID4VP](/docs/verification/oid4vp) | The request-and-response protocol used by a verifier to ask a wallet for a credential | OpenID4VP messages can be carried over redirects or over the DC API. ISO/IEC 18013-7 Annex D defines OpenID4VP over the DC API. |
| [ISO/IEC 18013-5](/docs/concepts/mdocs/standards-and-technologies) | mDoc data model and in-person, proximity-based presentation | The DC API is not used for in-person flows. ISO/IEC 18013-5 underpins the credential format and signatures that 18013-7 then carries online. |
| [ISO/IEC 18013-7](/docs/concepts/mdocs/standards-and-technologies) | Remote (online) verification of mDocs | Annex C defines mDoc device retrieval over the DC API. Annex D defines OpenID4VP over the DC API. |
In practice, this means a single DC API integration can serve both annexes of ISO/IEC
18013-7, and a verifier does not have to choose between OpenID4VP and the DC API. They
work together.
MATTR's implementation supports both annexes via a single integration in the Verifier Web
SDK, providing a consistent verification experience across platforms and browser types.
## How verification works with the DC API [#how-verification-works-with-the-dc-api]
A DC API-based verification follows four broad steps. This view focuses on what the
**verifier** does at each stage.
### Issue a verification request [#issue-a-verification-request]
When a user interacts with a verifier web application that requires a credential, the
verifier application initiates a DC API request through the user's browser. The browser
invokes the DC API, which communicates with the mobile device and shares a request object.
The request object includes a presentation definition detailing the information the
verifier requires, such as the type and format of credentials and any individual claims
from those credentials.
### The operating system gathers matching credentials [#the-operating-system-gathers-matching-credentials]
The mobile device queries all credentials that wallets have registered with the operating
system as available for DC API requests. Matching credentials are presented to the user in
a unified picker, regardless of which wallet application stores them.
From the verifier's point of view this stage is opaque. The verifier does not need to
know which wallet ultimately serves the request, the integration is the same in either
case.
### The wallet returns a verifiable presentation [#the-wallet-returns-a-verifiable-presentation]
Once the user selects a credential and consents to share it, the wallet sends an encrypted
verifiable presentation back through the DC API to the verifier application.
The browser ensures the response returns to the exact context where the request
originated, maintaining session continuity and security.
### The verifier validates the presentation [#the-verifier-validates-the-presentation]
Once the verifier receives the verifiable presentation, the application decrypts it and
applies verification checks to validate the credential's authenticity, the issuer's trust
status, and the response's compliance with the original presentation request.
The DC API supports both [same-device](#same-device-verification) and
[cross-device](#cross-device-verification) flows. A single DC API integration handles
both.
## Same-device verification [#same-device-verification]
Same-device flows involve a single mobile device running both the verifier web application
and the wallets that store the user's credentials.
For example, consider a user applying for a loan through their bank's mobile website.
When the site needs to verify the user's identity, it triggers a DC API request in the
browser. The browser uses the DC API to query all credentials registered on the device and
display the ones that can satisfy the request. The user authenticates, selects their
driver's license, and approves sharing it. The wallet then creates and signs the
presentation and returns it through the browser to the verifier application.
The DC API manages the entire flow within the browser context, eliminating the need for
redirects or leaving the user's current experience. The user authenticates using
platform-level biometrics, and the private key associated with the credential remains
secure in the device's key store.
## Cross-device verification [#cross-device-verification]
Cross-device flows involve two devices:
* One device (typically a desktop or laptop) runs the verifier application in a web
browser and creates the presentation request.
* A second device (typically a mobile phone) houses wallets that store the required
credentials and responds to the request.
The browser on the first device displays a QR code or other mechanism to initiate the
cross-device flow. The user scans this with their mobile device, which invokes the DC API
on the mobile platform. The mobile device queries registered credentials and presents them
to the user.
For example, a user completing a tax return on their desktop computer is asked to verify
their identity. The website displays a QR code, which the user scans with their phone. The
mobile device then presents matching registered credentials, the user selects their
national ID card, authenticates, and consents to sharing the information. The presentation
is sent back to the desktop browser, where verification completes and the tax return
process continues.
The DC API uses secure communication protocols with built-in proximity checks (such as
CTAP 2.1 over Bluetooth Low Energy) to verify the devices are physically close together.
This mitigates risks such as QR code replay attacks or session hijacking.
## Security and privacy properties [#security-and-privacy-properties]
The DC API provides several security and privacy advantages over redirect-based approaches
that are particularly relevant for verifiers:
* **Session continuity**: Once the interaction completes or if the user cancels, the
session continues in the original browser context without losing state or requiring
navigation. The verifier does not have to design around redirect bounces.
* **Proximity verification in cross-device flows**: Cross-device flows use operating
system-level proximity checks to ensure devices are physically near each other before
proceeding, reducing risks from QR code screenshots or remote attacks.
* **Reduced tracking surface**: The DC API prevents unauthorized apps from silently
tracking user interactions or accessing credential data without explicit user action,
which lowers the verifier's exposure to abuse vectors that target redirect-based flows.
## Platform support [#platform-support]
The DC API is a web browser standard and is implemented differently across platforms:
* **iOS**: Supported on Safari version 26 and later, available on iPhone 11 and later
running iOS 26.
* **Android**: Supported on Chrome and Chromium-based browsers version 138 and later.
The DC API is specifically designed for web applications running in browsers. Native
mobile applications use platform-specific APIs to interact with digital credentials. For
example, Android provides a platform-specific implementation called the DigitalCredential
API, which is part of the Credential Manager framework.
## How MATTR supports the DC API on the verifier side [#how-mattr-supports-the-dc-api-on-the-verifier-side]
The [MATTR VII Verifier Web SDK](/docs/verification/remote-web-verifiers/dc-api/guide)
embeds the DC API as a streamlined alternative to redirect-based OpenID4VP. The same SDK:
* Handles both **same-device** and **cross-device** flows with a single integration.
* Supports both **Annex C** (mDoc device retrieval over DC API) and **Annex D** (OpenID4VP
over DC API) of ISO/IEC 18013-7.
* Accepts presentations from **MATTR-built wallets**, **third-party wallets**, and **OEM
wallets** through one consistent integration.
This means a verifier integration built on MATTR VII does not need to be redesigned each
time a new wallet category becomes important. The DC API surface stays the same.
## Accepting credentials from OEM wallets [#accepting-credentials-from-oem-wallets]
One of the most significant capabilities the DC API unlocks is the ability to **accept
credentials from OEM wallets**. OEM wallets are credential wallets shipped natively on
mobile devices by their manufacturers, such as those provided by Apple and Google. They
are pre-installed, deeply integrated with the operating system, and increasingly used by
governments and large issuers to distribute high-assurance identity credentials such as
mobile driver's licenses and national identification cards.
Historically, integrating with an OEM wallet meant a separate, bespoke integration per
wallet provider. The DC API changes this. Because OEM wallets register their credentials
with the operating system just like any other DC API-compatible wallet, a verifier that
implements the DC API can accept presentations from OEM wallets through the same
integration it uses for any other wallet.
For organizations exploring this, the relevant questions are usually:
* Which OEM wallets are available in the jurisdictions and on the device platforms my
users rely on?
* Which credential formats do those OEM wallets support, and how do those map to the
credentials my verifier needs to accept?
* How do trust frameworks, issuer onboarding, and certification interact with OEM wallet
acceptance?
MATTR works with relying parties, governments, and ecosystem partners on exactly these
questions. If you are looking to accept credentials from OEM wallets, or to understand
which OEM wallets your verifier should support,
[contact us](mailto:dev-support@mattr.global) so we can talk through the specifics of your
deployment.
## Frequently asked questions [#frequently-asked-questions]
### What is the Digital Credentials API (DC API)? [#what-is-the-digital-credentials-api-dc-api]
The **Digital Credentials API (DC API)** is a web browser standard being incubated in the
W3C Web Incubator Community Group. It defines how websites and browsers can request and
present digital credentials directly through the browser, without redirecting the user to a
separate wallet app. The DC API is also incorporated into **ISO/IEC 18013-7** for remote
verification of mDocs.
### Why does the Digital Credentials API matter for web verification? [#why-does-the-digital-credentials-api-matter-for-web-verification]
The DC API removes long-standing friction in online credential verification. Instead of
asking users to pick between multiple wallet apps and bounce through redirects, the browser
shows a single prompt with credentials that actually match the request. This improves
completion rates, reduces dead-ends, and lets verifiers accept credentials from a wider set
of wallets, including those built into mobile operating systems.
### How does the DC API relate to OpenID4VP? [#how-does-the-dc-api-relate-to-openid4vp]
**OpenID4VP** is the request-and-response protocol that defines how a verifier asks for a
credential and how a wallet replies. The **DC API** is the browser transport that carries
those messages between the website and the wallet. OpenID4VP can be carried over redirects
or over the DC API, and ISO/IEC 18013-7 Annex D describes how OpenID4VP runs over the DC
API.
### How does the DC API relate to ISO/IEC 18013-7? [#how-does-the-dc-api-relate-to-isoiec-18013-7]
**ISO/IEC 18013-7** defines remote verification of mDocs. **Annex C** describes mDoc device
retrieval over the DC API (currently supported on iOS and Safari), and **Annex D**
describes OpenID4VP over the DC API (expected on Android and Chromium browsers). The DC
API is the browser-level mechanism both annexes rely on.
### Which browsers and platforms currently support the DC API? [#which-browsers-and-platforms-currently-support-the-dc-api]
The DC API is supported on **iOS 26 and later with Safari 26** (on iPhone 11 and later),
and on **Chrome and Chromium-based browsers version 138 and later** on Android.
Implementations are evolving, so the exact behavior can change as browsers and operating
systems update.
### Can verifiers accept credentials from OEM wallets via the DC API? [#can-verifiers-accept-credentials-from-oem-wallets-via-the-dc-api]
Yes. One of the most important capabilities the DC API unlocks is **accepting credentials
from OEM wallets**, which are wallets shipped natively by device manufacturers such as
Apple and Google. Because the DC API lets the operating system aggregate credentials from
any registered wallet, verifiers no longer need to integrate with each wallet
individually. MATTR's verification capabilities are designed to accept presentations from
OEM wallets via the DC API. To explore this for your deployment,
[contact us](mailto:dev-support@mattr.global).
### How does MATTR support the DC API on the verifier side? [#how-does-mattr-support-the-dc-api-on-the-verifier-side]
The **MATTR VII Verifier Web SDK** integrates the DC API as a streamlined alternative to
redirect-based OpenID4VP. A single integration handles both same-device and cross-device
flows, supports both Annex C and Annex D of ISO/IEC 18013-7, and lets verifiers accept
presentations from MATTR-built wallets, third-party wallets, and OEM wallets through one
consistent integration.
# Remote web verification DC API Workflow
URL: /docs/verification/remote-web-verifiers/dc-api/workflow
DC API support is currently offered as a tech preview. The Digital Credentials API specification
itself is still under active development in the W3C Web Incubator CG, and platform implementations
continue to evolve. As such, functionality may be limited, may not work in all scenarios, and could
change or break without prior notice as browsers and operating systems update their implementations.
mDocs are digital credentials based on the ISO/IEC
[18013-5](https://www.iso.org/standard/69084.html) standard and
[18013-7](https://www.iso.org/standard/91154.html) technical specification, designed to be stored on
a holder’s mobile device and support either in-person or remote
verification workflows.
The purpose of this page is to describe the end-to-end [remote web app verification](/docs/verification/remote-overview) DC API workflow, where a
user interacts with a web application to present an mDoc stored on their mobile device via the DC API, as per Annex C and D of ISO/IEC 18013-7:2025.
## Prerequisites [#prerequisites]
We recommend you make yourself familiar with the following concepts to support your understanding of
the implementation described in this page:
* What is [credential verification](/docs/verification)?
* What are [mDocs](/docs/concepts/mdocs)?
* What is [remote verification](/docs/verification/remote-overview)?
* What is the [DC API](/docs/verification/remote-web-verifiers/dc-api/overview)?
## Overview [#overview]
The DC API is a browser standard that enables web applications to request and verify
digital credentials directly from compatible wallet applications on the user's device. This provides a
seamless user experience within a platform-managed flow initiated from the browser. The API allows wallets to register as credential
providers with the mobile device's operating system, making them automatically discoverable when a website requests credentials.
## Detailed workflow [#detailed-workflow]
Let's take a closer look at the end-to-end workflow and explain the role of each component:
### Invoking the interaction [#invoking-the-interaction]
The user triggers an action in a verifier web application that requires presenting a credential for verification.
### Generating challenge [#generating-challenge]
When the verifier web application has a backend, the backend generates a unique challenge to ensure the security of the verification workflow. This challenge will be used to validate that the verification results received later are associated with this specific session.
Generating the challenge on the backend helps prevent tampering and replay attacks, as the challenge is not exposed to the frontend until verification is complete.
Furthermore, it enables the backend to associate the verification results with an existing session or transaction, enhancing the overall security and integrity of the verification process.
### Passing challenge to web application [#passing-challenge-to-web-application]
The verifier backend sends the generated challenge to the verifier web application, which will include it when starting the presentation session with MATTR VII.
### Checking browser support [#checking-browser-support]
The verifier web application checks if the user's browser supports the DC API.
### Requesting credentials [#requesting-credentials]
The **verifier web application** calls the SDK's `requestCredentials` method to start a presentation session with the configured **MATTR VII tenant**, including the challenge received from the backend.
### Creating presentation session [#creating-presentation-session]
The **MATTR VII tenant** creates a new presentation session and returns the request object to the **verifier web application**.
### Invoking DC API [#invoking-dc-api]
The **verifier web application** passes the request object to the user's **browser** to invoke the Digital Credentials API.
### Presenting verification request [#presenting-verification-request]
The **browser** presents a verification request interface to the **user**. This can be a QR code (when the user is on a desktop) or a button to start the process directly in the browser (when the user is on a mobile device).
### Initiating verification [#initiating-verification]
The **user** initiates the verification process by scanning the QR code/clicking the button.
### Forwarding request to mobile device [#forwarding-request-to-mobile-device]
The **browser** forwards the request to the **mobile device**.
### Displaying matching credentials [#displaying-matching-credentials]
The **mobile device** displays to the **user** matching credentials from installed **wallet applications** that are registered to handle DC API requests. This UI is rendered by the **mobile device** and is displayed on top of the web browser.
### Selecting credential [#selecting-credential]
The **user** selects a credential from the displayed options.
### Forwarding to wallet [#forwarding-to-wallet]
The **mobile device** forwards the verification request to the **wallet application** that holds the selected credential. On iOS devices, if there is only one compatible wallet that holds a matching credential, the system may directly open that wallet without displaying the selection UI to the user.
### Reviewing credential details [#reviewing-credential-details]
The mobile device displays the credential details to the **user** for review and consent. This UI is rendered by the **wallet application** and is displayed on top of the web browser in iOS, while on Android it is displayed within the system's native UI.
### Providing consent [#providing-consent]
The **user** reviews the credential details and provides consent to share them with the verifier.
### Returning presentation response [#returning-presentation-response]
The **wallet application** returns the encrypted credential as a presentation response to the **browser**.
### Forwarding to Verifier [#forwarding-to-verifier]
The **browser** forwards the presentation response to the **verifier web application**.
### Submitting for verification [#submitting-for-verification]
The **verifier web application** submits the presentation response to the **MATTR VII tenant** for verification.
### Verifying credential [#verifying-credential]
The **MATTR VII tenant** decrypts and verifies the credential.
### Notifying verification completion [#notifying-verification-completion]
The **MATTR VII tenant** notifies the **verifier web application** that verification has been completed. When the web application has a backend, the tenant does not send the verification results directly to the frontend for enhanced security.
### Passing session ID to backend [#passing-session-id-to-backend]
The **verifier web application** sends the presentation session ID to the **verifier backend** to retrieve the verification results securely.
### Retrieving verification results [#retrieving-verification-results]
The **verifier backend** makes a [request](/docs/verification/remote-verification-api-reference/presentation-sessions#retrieve-a-presentation-session-result) to **MATTR VII** to retrieve the verification results for the session using the session ID.
### Returning results with challenge [#returning-results-with-challenge]
The **MATTR VII tenant** responds with the verification results and the unique challenge that was included when the presentation session was created.
### Validating challenge [#validating-challenge]
The **verifier backend** compares the original challenge it generated with the challenge received from MATTR VII to ensure the response can be trusted and is associated with the correct session.
### Passing results to web application [#passing-results-to-web-application]
Once the challenge is validated, the **verifier backend** sends the verification results to the **verifier web application**.
### Displaying results [#displaying-results]
The **verifier web application** displays the verification results and the **user** continues the interaction accordingly.
Based on the unique challenge, the verifier can also continue any backend processes associated with the verification session, such as granting access to a service or completing a transaction.
# Correlating verification sessions with your system
URL: /docs/verification/remote-web-verifiers/guides/correlating-verification-sessions
Description: Learn how to use the optional state parameter to link a MATTR VII verification session to a record in your own application without implementing separate state management.
When a web application starts a remote verification session, MATTR VII generates its own
`sessionId` (a UUID) to track the session through its lifecycle. That identifier is fine for
referring to the session within MATTR VII, but it does not map to anything in your own system. If
you need to attach the verification to an existing record on your side, such as an onboarding
application, a checkout intent, or an account opening flow, you need a correlation reference of
your own.
The `state` parameter on `requestCredentials()` is intended for exactly this. You pass an opaque
string that means something in your system, and MATTR VII carries it through the verification
session and returns it to you with the result. No additional state management is required on
your application or backend.
## When to use `state` [#when-to-use-state]
Use `state` when your application has an existing record that a verification session needs to be
attached to, and you want a single value that:
* You generate and control.
* Travels through the entire session, including the wallet interaction.
* Comes back to you in both same-device and cross-device flows.
* Is available on both successful and failed results.
Typical examples include an onboarding application reference, a checkout or transaction
identifier, or any internal record ID that needs to be reconciled with the verification outcome.
If you only need to verify a credential and immediately act on the result in the same browser
session, `state` is not required. The MATTR VII `sessionId` is enough on its own. Use `state`
when correlation needs to survive the round-trip to the wallet and back.
The `state` value is transmitted in plain text as a query parameter on the wallet-facing
authorization request URI and is stored in session records. It should not contain personally
identifiable information (PII), credentials, or anything sensitive. Use an opaque reference,
such as an internal record ID or a randomly generated token your system can map back to the
underlying record.
## How `state` flows through a session [#how-state-flows-through-a-session]
When you supply `state`, MATTR VII threads the value through the OpenID4VP presentation flow:
1. The value is persisted with the session when it is created.
2. It is appended as a plain `&state={value}` query parameter on the wallet-facing
authorization request URI, making it readable to the wallet without parsing the signed
request object.
3. It is used as the OpenID4VP `state` claim inside the signed request object. When `state` is
not supplied, MATTR VII falls back to its internal `sessionId` for this claim.
4. The wallet's response is validated against the stored value. A mismatch results in a
failure result with a `VerificationError`, the same behavior as when no `state` is supplied
and a wallet echoes an unexpected value.
5. The value is returned to your application in the session result, on both success and
failure shapes.
This flow applies to OpenID4VP browser flows in both same-device and cross-device variants. It
does not apply to the Digital Credentials API flow.
## Setting `state` when starting a session [#setting-state-when-starting-a-session]
Pass `state` alongside the other options when calling `requestCredentials()` in the Verifier
Web SDK:
```typescript title="Verifier Web SDK example"
const options: MATTRVerifierSDK.RequestCredentialsOptions = {
credentialQuery: [credentialQuery],
challenge: MATTRVerifierSDK.utils.generateChallenge(),
state: "onboarding-app-7f3a4e8c", // [!code focus]
openid4vpConfiguration: {
redirectUri: window.location.origin,
walletProviderId: process.env.NEXT_PUBLIC_WALLET_PROVIDER_ID,
},
};
const results = await MATTRVerifierSDK.requestCredentials(options);
```
The value you choose is opaque to MATTR VII. Generate it in whatever way fits your system, for
example by reusing an existing internal record ID, by minting a fresh random token at the
moment you start the verification, or by deriving a one-way reference from a database row.
### Constraints [#constraints]
`state` is optional. When provided it should be a non-empty string of at most 256 characters.
A request that supplies an empty string or a value longer than 256 characters is rejected with
an HTTP 400 validation error.
The 256-character limit is enforced at the API boundary and is sufficient for any UUID, opaque
token, or short reference ID. If your internal identifier is longer, store it on your side and
pass a shorter reference that maps back to it.
## Reading `state` back [#reading-state-back]
`state` is returned everywhere a session result is exposed: in the SDK return values for both
same-device and cross-device flows, and in the back-channel result endpoint used by your
backend. It appears on both `PresentationSuccessResult` and `PresentationFailureResult`, so you
can correlate failures as well as successes.
### Cross-device flow (front channel) [#cross-device-flow-front-channel]
In cross-device flows, the Verifier Web SDK resolves results in the same call that started the
session. The `state` you supplied is echoed in the returned `RequestCredentialsResponse`:
```typescript
type RequestCredentialsResponse = {
sessionId: string;
state?: string;
result?: PresentationSessionResult;
sessionCompletedInRedirect?: boolean;
};
```
```typescript title="Reading state from the cross-device result"
const results = await MATTRVerifierSDK.requestCredentials(options);
if (results.isOk()) {
const { state, result } = results.value;
// state === "onboarding-app-7f3a4e8c"
// Use state to look up the matching record in your system.
}
```
### Same-device flow (front channel) [#same-device-flow-front-channel]
In same-device flows, the wallet redirects the user back to your `redirectUri` after the
presentation completes, and your application calls `handleRedirectCallback()` to retrieve the
result. The returned `state` is the same value you supplied at session creation:
```typescript
type HandleRedirectCallbackResponse = {
sessionId: string;
state?: string;
result?: PresentationSessionResult;
};
```
```typescript title="Reading state from the same-device redirect callback"
const results = await MATTRVerifierSDK.handleRedirectCallback();
if (results.isOk()) {
const { state, result } = results.value;
// state === "onboarding-app-7f3a4e8c"
}
```
The SDK persists `state` to `localStorage` before redirecting the browser to the wallet and
cleans it up after `handleRedirectCallback()` completes. This local persistence is an
implementation detail of the same-device flow and is needed because some relying-party
frameworks strip query parameters from redirect URIs. You do not need to read or write this
storage entry yourself.
### Back channel [#back-channel]
When your verifier application is configured with `resultAvailableInFrontChannel: false`, your
backend retrieves results by calling the
[retrieve presentation session result](/docs/api-reference/platform/mdocs-presentation-sessions/getPresentationResult)
endpoint. The response body includes the same `state` field, present only when a value was
supplied at session creation:
```json title="Example back-channel result with state"
{
"sessionId": "550e8400-e29b-41d4-a716-446655440000",
"challenge": "c5a27e4c-85b6-4b3c-9f1a-2d8e5f3a4b7c",
"state": "onboarding-app-7f3a4e8c",
"credentialQuery": [
{
"profile": "mobile",
"docType": "org.iso.18013.5.1.mDL",
"nameSpaces": {
"org.iso.18013.5.1": {
"family_name": { "intentToRetain": false },
"given_name": { "intentToRetain": false }
}
}
}
],
"credentials": [
{
"docType": "org.iso.18013.5.1.mDL",
"claims": {
"org.iso.18013.5.1": {
"family_name": { "value": "Smith" },
"given_name": { "value": "Jane" }
}
},
"verificationResult": { "verified": true }
}
]
}
```
The same field appears on failure results, allowing you to correlate failed sessions to your
internal record:
```json title="Example back-channel failure result with state"
{
"sessionId": "550e8400-e29b-41d4-a716-446655440000",
"challenge": "c5a27e4c-85b6-4b3c-9f1a-2d8e5f3a4b7c",
"state": "onboarding-app-7f3a4e8c",
"credentialQuery": [
{
"profile": "mobile",
"docType": "org.iso.18013.5.1.mDL",
"nameSpaces": {
"org.iso.18013.5.1": {
"family_name": { "intentToRetain": false }
}
}
}
],
"error": {
"type": "SessionAborted",
"message": "User aborted the session"
}
}
```
`state` is a correlation reference, not a security mechanism. It does not replace the
challenge used to detect session replay. When using back-channel delivery, your backend
should still generate a unique `challenge` per session and validate the value returned in
the result against the one you stored. See the
[handling verification results guide](/docs/verification/remote-web-verifiers/guides/handling-verification-results)
for the full back-channel pattern.
## A worked example [#a-worked-example]
A common pattern is to mint an internal reference when a user starts a verification step,
record it against the user's session in your system, pass it as `state`, and use it on the
return trip to find the right record.
```typescript title="Web application"
// Mint or retrieve an internal reference for this user's onboarding step.
const applicationRef = await fetch("/api/onboarding/start", { method: "POST" })
.then((r) => r.json())
.then((r) => r.applicationRef);
const options: MATTRVerifierSDK.RequestCredentialsOptions = {
credentialQuery: [credentialQuery],
challenge: await createChallenge(),
state: applicationRef,
openid4vpConfiguration: {
redirectUri: `${window.location.origin}/onboarding/complete`,
walletProviderId: process.env.NEXT_PUBLIC_WALLET_PROVIDER_ID,
},
};
await MATTRVerifierSDK.requestCredentials(options);
```
```typescript title="Same-device callback handler"
const results = await MATTRVerifierSDK.handleRedirectCallback();
if (results.isOk()) {
const { sessionId, state, result } = results.value;
// Hand sessionId and state to your backend so it can fetch the result
// and attach it to the correct application record.
await fetch("/api/onboarding/complete", {
method: "POST",
body: JSON.stringify({ sessionId, applicationRef: state }),
});
}
```
```typescript title="Backend result handler"
// In your backend handler for /api/onboarding/complete:
const result = await fetchPresentationResult(sessionId);
if (result.challenge !== storedChallengeFor(sessionId)) {
throw new Error("Challenge mismatch");
}
// result.state is the same applicationRef the web application supplied.
await applications.attachVerificationResult(result.state, result);
```
This pattern lets your backend reconcile the verification outcome with the originating
application record without maintaining a separate `sessionId`-to-application mapping.
## When `state` is not supplied [#when-state-is-not-supplied]
`state` is fully optional and additive. If you do not pass it:
* No `&state=` query parameter is added to the authorization request URI.
* The OpenID4VP `state` claim in the signed request object falls back to the MATTR VII
`sessionId`.
* The same-device redirect URI is unchanged.
* No `state` field appears in any result, on either success or failure shapes.
Existing integrations that do not pass `state` will see no change in behavior.
## Next steps [#next-steps]
* Review the [Verifier Web SDK API reference](https://api-reference-sdk.mattr.global/verifier-sdk-web/latest/)
for complete type definitions.
* See the [handling verification results guide](/docs/verification/remote-web-verifiers/guides/handling-verification-results)
for the broader result structure and the back-channel challenge validation pattern.
* See the [workflow guide](/docs/verification/remote-web-verifiers/workflow) for where the
`state` value sits within the end-to-end remote verification flow.
# Handling verification results
URL: /docs/verification/remote-web-verifiers/guides/handling-verification-results
Description: Learn how to retrieve and interpret mDoc verification results in your web application
When a holder responds to a web verification request, that response includes structured data containing the verification outcome. This guide explains the different types of responses your web application can receive as a verifier and how to access them.
## What gets verified [#what-gets-verified]
Before working through the response structure, it is worth being clear about what is actually
being verified. The credential itself, the full mDoc as issued and stored in the holder's
wallet, never leaves the wallet and is never shared with a verifier. What the holder shares is
a *credential presentation*: a subset of the credential's data, accompanied by the issuer's
signature over the released items and a device authentication that proves the holder's device
authorized this specific presentation.
This model is grounded in [ISO/IEC 18013-7](https://www.iso.org/standard/91154.html), which
defines remote mDL and mDoc verification and specifies the wire protocols used to exchange a
presentation between holder and verifier: the OpenID for Verifiable Presentations (OID4VP)
protocol (Annex B), and the W3C Digital Credentials (DC) API integration (Annex C and Annex D).
Regardless of the wire protocol, what the holder shares is a presentation derived from the
credential rather than the credential itself, and the presentation carries the same
cryptographic proof and integrity guarantees as the credential it is drawn from.
When you verify a response, you are verifying the credential presentation. The cryptographic
trust checks (issuer signature, issuer trust, validity, revocation, device key binding) apply
to that presented slice, not to the credential as a whole on the holder's device. The MATTR VII
`verificationResult.verified` flag reflects whether this presented credential passed those
checks.
The rest of this guide uses "credential" as shorthand for the presented credential when the
context makes clear we are talking about what was returned in the response.
## Result delivery methods [#result-delivery-methods]
The way your web application receives verification results depends on your MATTR VII verifier application configuration:
**Front channel delivery** (`resultAvailableInFrontChannel: true`): Results are returned directly to your web application through the Verifier Web SDK as a [`RequestCredentialsResponse`](https://api-reference-sdk.mattr.global/verifier-sdk-web/latest/types/RequestCredentialsResponse.html) object.
**Back channel delivery** (`resultAvailableInFrontChannel: false`): Your backend retrieves results by calling the [retrieve presentation session result](/docs/api-reference/platform/mdocs-presentation-sessions/getPresentationResult) endpoint, receiving them as a `200` response.
For more details on how these delivery methods work within the complete verification flow, see the [workflow guide](/docs/verification/remote-web-verifiers/workflow).
Production implementations should use back channel delivery with a backend. This provides better security through challenge validation, protecting against session replay attacks.
Regardless of the delivery method, the same information is available in both response structures. The key difference is where and how you retrieve it.
## Understanding response types [#understanding-response-types]
When a holder responds to a verification request, you receive one of two high-level result types, depending on whether the presentation workflow was completed successfully or not.
### Presentation failed [#presentation-failed]
In this case, the holder was unable to complete the presentation workflow. This can happen for various reasons, such as the user canceling the request, the wallet being unavailable, or an error occurring during presentation processing.
You will receive a `PresentationFailureResult` object containing:
* **`sessionId`**: The unique identifier for this verification session
* **`challenge`**: The challenge used to verify response authenticity
* **`credentialQuery`**: The original request defining what credentials and claims were requested
* **`error`**: Object with `type` and `message` fields
* **`state`** (optional): The caller-supplied correlation reference, present only when a `state` value was provided when starting the session. See [Correlating verification sessions](/docs/verification/remote-web-verifiers/guides/correlating-verification-sessions).
The `error.type` indicates why the presentation failed:
* **`WalletUnavailable`**: The wallet appears to be unavailable and unable to respond to the request
* **`SessionAborted`**: User explicitly aborted the session before it timed out
* **`ResponseError`**: Received an error presentation response
* **`VerificationError`**: The submitted presentation response is invalid
* **`Unknown`**: Encountered an unknown error while processing the presentation response
**Example**: User cancels the mDL presentation request:
```json title="Example presentation failure response for user cancellation"
{
"sessionId": "550e8400-e29b-41d4-a716-446655440000",
"challenge": "c5a27e4c-85b6-4b3c-9f1a-2d8e5f3a4b7c",
"credentialQuery": [
{
"profile": "mobile",
"docType": "org.iso.18013.5.1.mDL",
"nameSpaces": {
"org.iso.18013.5.1": {
"family_name": { "intentToRetain": false },
"given_name": { "intentToRetain": false },
"birth_date": { "intentToRetain": false }
}
}
}
],
"error": {
"type": "SessionAborted",
"message": "User aborted the session"
}
}
```
### Presentation succeeded [#presentation-succeeded]
In this case, the holder successfully completed the presentation workflow. You will receive a `PresentationSuccessResult` object containing:
* **`sessionId`**: The unique identifier for this verification session
* **`challenge`**: The challenge used to verify response authenticity
* **`credentialQuery`**: The original request defining what credentials and claims were requested
* **`credentials`** (optional): Array of verified credentials with their claims and verification status
* **`credentialErrors`** (optional): Array of errors for credentials that couldn't be returned
* **`state`** (optional): The caller-supplied correlation reference, present only when a `state` value was provided when starting the session. See [Correlating verification sessions](/docs/verification/remote-web-verifiers/guides/correlating-verification-sessions).
A successful presentation can include several scenarios, often in combination. Each one is
described along two independent axes: whether the requested credential itself verified, and
whether the requested claims within it were provided.
1. **Requested credential not presented**: The holder doesn't have the requested credential, so there is nothing to verify. The credential appears under `credentialErrors` with an error code of `notReturned`, and no `verificationResult` is produced.
2. **Presented credential not verified**: A credential was provided but the credential-level trust checks failed. The `credentials` array contains the credential with `verified: false` and a `reason.type` explaining why. Even if claims were returned, they should not be relied on while the credential itself did not verify.
3. **Presented credential verified, all claims provided**: The credential-level checks pass (`verified: true` with no `reason`) and every requested claim is returned (no `claimErrors`). Both layers are clean.
4. **Presented credential verified, some claims missing**: The credential-level checks pass (`verified: true`), but one or more requested claims were not returned and appear under `claimErrors`. The credential is trustworthy; the data payload is incomplete. How to handle the missing data is up to the relying party, depending on its use case. Options include failing the flow, falling back to collecting the required data through another channel, or proceeding if the missing claim is not essential.
## Detailed result structure [#detailed-result-structure]
When a presentation succeeds, the result includes detailed information at multiple levels:
### Credential-level information [#credential-level-information]
Each credential in the `credentials` array contains:
* **`docType`**: The credential type (e.g., `org.iso.18013.5.1.mDL` for a mobile driver's license).
* **`claims`**: Verified claims organized by namespace.
* **`claimErrors`**: Errors for individual claims that couldn't be verified or were not returned.
* **`validityInfo`**: Credential validity period timestamps.
* **`verificationResult`**: Verification status containing:
* **`verified`**: Boolean indicating if verification succeeded (this is a high-level result and individual claim errors may still exist).
* **`reason`** (optional): Object with `type` and `message` explaining verification failures (if such failures exist). The following `reason.type` values are possible:
* `DeviceKeyInvalid`: Device key is not valid. This can occur if the credential was not properly bound to the device or if the device's secure element is compromised.
* `InvalidSignerCertificate`: Invalid signer certificate. This can occur if the credential's signing certificate is not valid or has been tampered with.
* `IssuerNotTrusted`: Credential was issued by a certificate that is not trusted by the verifier. This can occur if the issuer's certificate is not included in the verifier's trusted issuer list.
* `MobileCredentialExpired`: Credential expired. This can occur if the current date is after the credential's `validUntil` date.
* `MobileCredentialInvalid`: Credential is not valid. This can occur for various reasons such as failing signature verification, containing invalid data, or not conforming to expected formats.
* `MobileCredentialNotYetValid`: Credential not yet valid. This can occur if the current date is before the credential's `validFrom` date.
* `StatusRevoked`: Credential has been revoked. This can occur if the issuer has revoked the credential after it was issued, which is typically checked through a revocation mechanism provided by the issuer.
* `StatusUnknown`: Credential status could not be determined. This can occur if the verifier is unable to check the revocation status of the credential due to network issues or if the issuer does not provide a revocation mechanism.
* `TrustedIssuerCertificateExpired`: Trusted issuer certificate expired. This can occur if the certificate of the trusted issuer has passed its expiration date, which may affect the trustworthiness of credentials issued by that issuer.
* `TrustedIssuerCertificateNotYetValid`: Trusted issuer certificate not yet valid. This can occur if the certificate of the trusted issuer is not yet valid (i.e., the current date is before the certificate's `validFrom` date), which may affect the trustworthiness of credentials issued by that issuer.
* `UnsupportedCurve`: Credential object contains unsupported curve. This can occur if the credential uses cryptographic curves that are not supported by the verifier's cryptographic library, which may prevent successful verification of the credential's signatures.
* **`issuerInfo`**: Issuer details including `commonName` and `trustedIssuerId`.
* **`branding`** (optional): Visual information for displaying the credential (name, description, colors, logos). You can use this information to create a rich user interface when showing the credential details in your application.
### Claim-level information [#claim-level-information]
Claims are organized by namespace within the `claims` object. For example, for an mDL, claims will appear under the `org.iso.18013.5.1` namespace:
```json title="Example claims structure for a verified mDL credential"
"claims": {
"org.iso.18013.5.1": {
"family_name": { "value": "Smith" },
"given_name": { "value": "Jane" },
"birth_date": { "value": "1990-05-15" },
"address": { "value": "123 Main Street, Springfield" }
}
}
```
If specific claims couldn't be verified or weren't provided, they appear in the `claimErrors` object with the same namespace structure and an error code of `notReturned`:
```json title="Example claim errors structure for a credential with a missing claim"
"claimErrors": {
"org.iso.18013.5.1": {
"portrait": "notReturned"
}
}
```
### Credential errors [#credential-errors]
When a requested credential wasn't provided by the holder, it appears in the `credentialErrors` array with a `docType` and an `errorCode` of `notReturned`:
```json title="Example credential errors structure for a missing credential"
"credentialErrors": [
{
"docType": "org.iso.18013.5.1.mDL",
"errorCode": "notReturned"
}
]
```
## Understanding the `verified` flag [#understanding-the-verified-flag]
With the structure in mind, it is worth being explicit about what
`verificationResult.verified` does and does not tell you, because it is the single most important
field for deciding whether to trust a presentation.
`verified` is the pass/fail flag for the presented credential. It applies to the slice of
credential data the holder released in this session, together with its associated issuer
signature and device authentication, not to the credential as a whole as stored on the holder's
device. When `verified: true`, what the holder presented has passed MATTR VII's cryptographic
and trust checks: the issuer signature on the released items is intact, the credential was
issued by a trusted issuer, it is not expired or revoked, and the device key binding holds. In
plain terms, the presented mDoc is genuine, has not been tampered with, and is currently valid.
What `verified: true` does not mean is that every claim you requested came back. `verified` is a
high-level result on the credential as a whole, and individual claim errors may still exist. A
credential can return `verified: true` while a specific requested claim (for example, `portrait`)
is missing and surfaces under `claimErrors` with an error code of `notReturned`. The credential
is trustworthy; the data payload may still be incomplete.
The mirror image is `verified: false`. When the credential layer fails, MATTR VII surfaces a
`reason.type` explaining why (for example, expired, revoked, untrusted issuer, invalid signer
certificate, or broken device key binding). The `reason` field is typed as optional, so
defensive code should handle the case where it is absent. See the
[credential-level information](#credential-level-information) section above for the full list of
`reason.type` values.
### Two layers of checks [#two-layers-of-checks]
A relying party's business logic needs to check two distinct things:
1. **Is the credential real and valid?** Look at `verificationResult.verified` on each credential
in the `credentials` array. This is the objective trust check, and it should not be overridden
by business logic.
2. **Did we get all the data we asked for?** Look at `claimErrors` on each credential, and at
`credentialErrors` on the top-level result. Whether a missing claim or credential is
acceptable is a business-logic decision specific to your use case.
In other words, `verified` is the objective measure of what can or cannot be accepted at all.
Everything else, including `claimErrors`, `credentialErrors`, and the specific claim values
returned, feeds your business logic about whether to accept the presentation for your particular
use case.
Treating `verified: true` as a green light to proceed without inspecting `claimErrors` is a
common integration mistake. The credential may be cryptographically valid while still missing a
field your use case requires. For example, an age-restricted purchase flow needs `birth_date`,
but if only `family_name` came back the relying party cannot make an age decision even though
the credential itself returned `verified: true`. Conversely, bypassing a `verified: false`
result with business logic (for example, accepting an expired or revoked credential because the
claims look correct) defeats the trust model and should not be done.
`verified` is MATTR VII's API shape. The underlying trust evaluation for mDL and mDoc
credentials follows ISO/IEC 18013-5, but the `verified` boolean itself is not a standards-defined
field.
## Complete examples [#complete-examples]
### Requested credential not presented [#requested-credential-not-presented]
Neither layer of the check produces a result here. The holder did not provide a matching mDL, so
the credential layer has nothing to verify (no `verificationResult` is returned) and the claims
layer is not applicable. The missing credential is reported under `credentialErrors`, and the
relying party's business logic must decide how to handle it.
```json title="Example response for a requested credential that was not presented"
{
"sessionId": "550e8400-e29b-41d4-a716-446655440000",
"challenge": "c5a27e4c-85b6-4b3c-9f1a-2d8e5f3a4b7c",
"credentialQuery": [
{
"profile": "mobile",
"docType": "org.iso.18013.5.1.mDL",
"nameSpaces": {
"org.iso.18013.5.1": {
"family_name": { "intentToRetain": false },
"given_name": { "intentToRetain": false }
}
}
}
],
"credentialErrors": [
{
"docType": "org.iso.18013.5.1.mDL",
"errorCode": "notReturned"
}
]
}
```
### Presented credential not verified [#presented-credential-not-verified]
The credential layer fails. The holder provided an mDL but the credential-level trust checks
failed (in this example, because the credential has expired): `verified: false`, with
`reason.type` identifying why. Even if claims were returned, they should not be relied on while
the credential itself did not verify. The credential should not be accepted regardless of
business logic.
```json title="Example response for a presented credential that did not verify (expired mDL)"
{
"sessionId": "550e8400-e29b-41d4-a716-446655440000",
"challenge": "c5a27e4c-85b6-4b3c-9f1a-2d8e5f3a4b7c",
"credentialQuery": [
{
"profile": "mobile",
"docType": "org.iso.18013.5.1.mDL",
"nameSpaces": {
"org.iso.18013.5.1": {
"family_name": { "intentToRetain": false },
"given_name": { "intentToRetain": false },
"birth_date": { "intentToRetain": false }
}
}
}
],
"credentials": [
{
"docType": "org.iso.18013.5.1.mDL",
"verificationResult": {
"verified": false,
"reason": {
"type": "MobileCredentialExpired",
"message": "Credential has expired"
}
},
"validityInfo": {
"signed": "2024-01-15T10:00:00Z",
"validFrom": "2024-01-15T10:00:00Z",
"validUntil": "2025-01-15T10:00:00Z",
"expectedUpdate": "2026-01-15T10:00:00Z"
}
}
]
}
```
### Presented credential verified, all claims provided [#presented-credential-verified-all-claims-provided]
Both layers pass. The credential layer returns `verified: true` with no `reason`, confirming the
mDL is genuine, current, and from a trusted issuer. The claims layer returns every requested
claim with no `claimErrors`, so the relying party has the complete data payload it asked for.
```json title="Example response for a presented credential that verified with all requested claims returned"
{
"sessionId": "550e8400-e29b-41d4-a716-446655440000",
"challenge": "c5a27e4c-85b6-4b3c-9f1a-2d8e5f3a4b7c",
"credentialQuery": [
{
"profile": "mobile",
"docType": "org.iso.18013.5.1.mDL",
"nameSpaces": {
"org.iso.18013.5.1": {
"family_name": { "intentToRetain": false },
"given_name": { "intentToRetain": false },
"birth_date": { "intentToRetain": false },
"address": { "intentToRetain": false }
}
}
}
],
"credentials": [
{
"docType": "org.iso.18013.5.1.mDL",
"claims": {
"org.iso.18013.5.1": {
"family_name": { "value": "Smith" },
"given_name": { "value": "Jane" },
"birth_date": { "value": "1990-05-15" },
"address": { "value": "123 Main Street, Springfield" }
}
},
"validityInfo": {
"signed": "2024-01-15T10:00:00Z",
"validFrom": "2024-01-15T10:00:00Z",
"validUntil": "2027-01-15T10:00:00Z",
"expectedUpdate": "2028-01-15T10:00:00Z"
},
"verificationResult": {
"verified": true
},
"issuerInfo": {
"commonName": "State Department of Motor Vehicles",
"trustedIssuerId": "d4a6e9f2-3b1c-4d8e-a5f7-9c2b0e8d1a3f"
},
"branding": {
"name": "Driver License",
"description": "State-issued driver license",
"backgroundColor": "#1E3A8A",
"issuerLogo": {
"format": "svg",
"data": "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg=="
}
}
}
]
}
```
### Presented credential verified, some claims missing [#presented-credential-verified-some-claims-missing]
The credential layer passes but the claims layer is incomplete. The mDL itself returns
`verified: true` and is trustworthy, while one requested claim (`portrait`) was not released and
appears under `claimErrors` as `notReturned`. What is missing here is data, not trust, and the
relying party's business logic must decide whether the missing claim is acceptable for its use
case.
```json title="Example response for a presented credential that verified with a missing claim"
{
"sessionId": "550e8400-e29b-41d4-a716-446655440000",
"challenge": "c5a27e4c-85b6-4b3c-9f1a-2d8e5f3a4b7c",
"credentialQuery": [
{
"profile": "mobile",
"docType": "org.iso.18013.5.1.mDL",
"nameSpaces": {
"org.iso.18013.5.1": {
"family_name": { "intentToRetain": false },
"given_name": { "intentToRetain": false },
"birth_date": { "intentToRetain": false },
"portrait": { "intentToRetain": false }
}
}
}
],
"credentials": [
{
"docType": "org.iso.18013.5.1.mDL",
"claims": {
"org.iso.18013.5.1": {
"family_name": { "value": "Smith" },
"given_name": { "value": "Jane" },
"birth_date": { "value": "1990-05-15" }
}
},
"claimErrors": {
"org.iso.18013.5.1": {
"portrait": "notReturned"
}
},
"validityInfo": {
"signed": "2024-01-15T10:00:00Z",
"validFrom": "2024-01-15T10:00:00Z",
"validUntil": "2027-01-15T10:00:00Z",
"expectedUpdate": "2028-01-15T10:00:00Z"
},
"verificationResult": {
"verified": true
},
"issuerInfo": {
"commonName": "State Department of Motor Vehicles",
"trustedIssuerId": "d4a6e9f2-3b1c-4d8e-a5f7-9c2b0e8d1a3f"
}
}
]
}
```
## Front channel vs back channel details [#front-channel-vs-back-channel-details]
### Front channel delivery [#front-channel-delivery]
When using `resultAvailableInFrontChannel: true`, the Verifier Web SDK's `requestCredentials` function returns a [`RequestCredentialsResponse`](https://api-reference-sdk.mattr.global/verifier-sdk-web/latest/types/RequestCredentialsResponse.html):
```typescript
type RequestCredentialsResponse = {
sessionId: string;
state?: string;
result?: PresentationSessionResult;
sessionCompletedInRedirect?: boolean;
};
```
Where `result` contains either a `PresentationSuccessResult` or `PresentationFailureResult` as detailed above.
For same-device flows, the result is delivered via `handleRedirectCallback()` after the wallet redirects the user back to your application:
```typescript
type HandleRedirectCallbackResponse = {
sessionId: string;
state?: string;
result?: PresentationSessionResult;
};
```
The optional `state` field is the caller-supplied correlation reference returned when a `state` value was provided when starting the session. See [Correlating verification sessions](/docs/verification/remote-web-verifiers/guides/correlating-verification-sessions) for details.
### Back channel delivery [#back-channel-delivery]
When your backend retrieves results from the MATTR VII tenant:
```
GET /v2/presentations/sessions/{sessionId}/result
```
The response is either a `PresentationSuccessResult` or `PresentationFailureResult` with the same structure as front channel results.
**Backend implementation steps**:
1. Retrieve verification results using the session ID
2. Validate the returned `challenge` matches the challenge generated before starting the session
3. Process the results according to your business logic
4. Return relevant information to your web application
Challenge validation in the backend protects against session replay attacks. Generate a unique challenge when creating the session, and verify it matches when retrieving results.
## Next steps [#next-steps]
* Review the [Verifier Web SDK API reference](https://api-reference-sdk.mattr.global/verifier-sdk-web/latest/) for complete type definitions
* See the [presentation session result API reference](/docs/api-reference/platform/mdocs-presentation-sessions/getPresentationResult) for backend integration
* Explore the [workflow guide](/docs/verification/remote-web-verifiers/workflow) for the complete end-to-end verification process
* Follow the [tutorial](/docs/verification/remote-web-verifiers/tutorial) for building a remote web verifier with the MATTR VII tenant and Verifier Web SDK for practical implementation examples
# Controlling wallet interactions with URI schemes
URL: /docs/verification/remote-web-verifiers/guides/oid4vp-wallet-interactions
Description: Learn how to control which wallet applications can interact with your verification requests using URI schemes, and how to implement different schemes for various user experience and business requirements.
## Overview [#overview]
When implementing an online verification workflow using OID4VP, your MATTR VII verifier tenant generates verification request URIs that invoke wallet applications to present credentials. These request URIs can be presented to users as QR codes (for cross-device flows) or deep links (for same-device flows).
As a verifier, you can control which wallet applications can interact with your verification requests and influence the user experience through URI schemes. This involves both user experience considerations and business requirements where you may want to work with specific wallet providers.
## Controlling which wallets can respond to verification requests [#controlling-which-wallets-can-respond-to-verification-requests]
The mechanism for controlling which wallets can interact with your verification requests is through URI schemes and trusted wallet configurations. When you create a presentation session on your MATTR VII tenant, you can specify which wallet provider should handle the request, and MATTR VII will generate the verification request URI using that wallet's configured URI scheme.
### Understanding URI schemes [#understanding-uri-schemes]
A URI scheme is the protocol part at the beginning of a URI (such as `https://`, `mailto:`, or custom schemes like `mdoc-openid4vp://`). The URI scheme you choose affects:
* **Which wallet apps can respond to the request**: Some schemes allow any compatible wallet app, while others target specific wallet applications.
* **User experience**: How smoothly users can present credentials and whether they see app selection prompts.
* **Business alignment**: Your ability to work with specific wallet providers that align with your requirements.
Several URI scheme types can be used for verification requests:
1. **ISO 18013-7 default scheme** (`mdoc-openid4vp://`): Defined by ISO/IEC 18013-7:2025. A wallet that handles this scheme declares support for the static wallet metadata parameters defined in ISO 18013-7. Any wallet app registered to handle this scheme can respond to the verification request.
2. **Private-use URI scheme** (`com.example.wallet://`): A unique custom scheme using reverse-domain notation. This makes it less likely (but not impossible) for other wallet apps to handle the request. Unlike the default schemes above, a private-use scheme carries no implied protocol configuration — you must either know the wallet's supported capabilities out-of-band, or discover them dynamically via [wallet metadata](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-wallet-metadata-authorizati).
3. **Claimed HTTPS scheme** (`https://wallet.example.com/...`): Uses domain-verified App Links (Android) or Universal Links (iOS) to ensure only the verified wallet app can handle verification requests from that domain.
### Choosing the right URI scheme [#choosing-the-right-uri-scheme]
Select a URI scheme based on your requirements:
| Requirement | Recommended Scheme |
| ------------------------------------------------------------ | ---------------------------------------- |
| ISO 18013-7 compliant wallets (e.g. mobile driving licences) | ISO 18013-7 scheme (`mdoc-openid4vp://`) |
| Development and testing | ISO 18013-7 |
| Target a specific wallet application | Private-use URI scheme |
| Business partnerships with specific wallet providers | Private-use or Claimed HTTPS scheme |
| Work with wallets that support Universal/App Links | Claimed HTTPS scheme |
### Implementing URI schemes [#implementing-uri-schemes]
The following sections show you how to implement each URI scheme type as a verifier.
#### ISO 18013-7 default scheme (`mdoc-openid4vp://`) [#iso-18013-7-default-scheme-mdoc-openid4vp]
The `mdoc-openid4vp://` scheme is defined by ISO/IEC 18013-7:2025. By using this scheme, you signal to wallets that your verification request expects ISO 18013-7 protocol compliance — including support for ECDH-ES as `authorization_encryption_alg_values_supported`. Any wallet registered to handle this scheme can respond.
**When to use:**
* You are verifying ISO 18013-7 credentials (such as mobile driving licences).
* You want to support any wallet that claims ISO 18013-7 compliance.
* You're developing or testing an ISO 18013-7 verification flow.
**How it works:**
MATTR VII generates verification request URIs using this scheme when you configure a wallet provider with this authorization endpoint.
**Implementation:**
1. Create a supported wallet configuration using the MATTR Portal or API (optional - this is the default behavior):
1) Expand the **Credential Verification** section in the left-hand navigation panel.
2) Select **Supported wallets**.
3) Select the **Create new** button.
4) Use the *Name* text box to insert a meaningful and friendly name for your wallet application (for
example "MATTR GO Hold").
5) Use the *Authorize endpoint* text box to insert `mdoc-openid4vp://`.
6) Select the **Create** button to create the new wallet configuration.
7) Note the wallet provider ID for this configuration, which you can use when creating presentation sessions to indicate that this is the wallet you expect to receive mDocs from.
Make the following request to your MATTR VII tenant to
[create a trusted wallet provider configuration](/docs/verification/remote-verification-api-reference/wallet-providers#create-a-wallet-provider):
```http title="Request"
POST /v2/presentations/wallet-providers
```
```json title="Request body"
{
"name": "MATTR GO Hold", // [!code focus]
"openid4vpConfiguration": {
"authorizationEndpoint": "mdoc-openid4vp://" // [!code focus]
}
}
```
*Response*
```json title="Response body"
{
"id": "99890c34-e4b7-4a23-84d6-e5de57114c00", // [!code focus]
"name": "MATTR GO Hold",
"openid4vpConfiguration": {
"authorizationEndpoint": "mdoc-openid4vp://"
}
}
```
* `id` : We will use this value later to indicate this is the wallet the web application expects to
receive mDocs from.
2. [Start a presentation session](/docs/verification/remote-web-verifiers/tutorial#create-credential-request) using the Verifier Web SDK or API, optionally referencing the wallet provider ID:
```typescript title="Verifier Web SDK example"
const options: MATTRVerifierSDK.RequestCredentialsOptions = {
credentialQuery: [credentialQuery],
challenge: MATTRVerifierSDK.utils.generateChallenge(),
openid4vpConfiguration: {
redirectUri: window.location.origin,
walletProviderId: "99890c34-e4b7-4a23-84d6-e5de57114c00" // Optional
},
};
const results = await MATTRVerifierSDK.requestCredentials(options);
```
3. The SDK will return a verification request URI and automatically render it as a QR code (cross-device flows) or deep link (same-device flows).
**Trade-offs:**
* ✅ Works with any ISO 18013-7 compliant wallet.
* ✅ Standards-compliant for ISO-based credential flows.
* ⚠️ Users may see multiple wallet options if they have several installed.
* ⚠️ Cannot target a specific wallet application.
**Requirements for wallet applications:**
For wallet apps to respond to these requests, they must be configured to handle the `mdoc-openid4vp://` scheme and meet the full ISO 18013-7 protocol requirements. See the [holder guide](/docs/holding/remote-presentation-guides/handling-uri-schemes#iso-18013-7-default-scheme-mdoc-openid4vp) for implementation details.
#### Private-use URI scheme [#private-use-uri-scheme]
A private-use URI scheme uses reverse-domain notation (e.g., `com.example.wallet://`) to target a specific wallet application. This allows you to direct verification requests to a particular wallet provider that uses a custom URI scheme.
**When to use:**
* You want to work with a specific wallet provider that uses a custom URI scheme.
* You're partnering with a wallet provider and want to direct users to their application.
* You need better targeting than the standard scheme but the wallet doesn't support domain-verified links.
**How it works:**
You configure the wallet provider's custom URI scheme as their authorization endpoint on your MATTR VII tenant. When you create a presentation session and reference that wallet provider, MATTR VII generates the verification request URI using the configured scheme.
**Implementation:**
1. Create a supported wallet configuration with the wallet provider's custom URI scheme. You can do this via the MATTR Portal or by making an API request:
1) Expand the **Credential Verification** section in the left-hand navigation panel.
2) Select **Supported wallets**.
3) Select the **Create new** button.
4) Use the *Name* text box to insert a meaningful and friendly name for your wallet application (for
example "Partner Wallet App").
5) Use the *Authorize endpoint* text box to insert the private-use URI scheme, for example `com.example.wallet://`.
6) Select the **Create** button to create the new wallet configuration.
7) Note the wallet provider ID for this configuration, which you can use when creating presentation sessions to indicate that this is the wallet you expect to receive mDocs from.
Make the following request to your MATTR VII tenant to
[create a trusted wallet provider configuration](/docs/verification/remote-verification-api-reference/wallet-providers#create-a-wallet-provider):
```http title="Request"
POST /v2/presentations/wallet-providers
```
```json title="Request body"
{
"name": "Partner Wallet App", // [!code focus]
"openid4vpConfiguration": {
"authorizationEndpoint": "com.example.wallet://" // [!code focus]
}
}
```
*Response*
```json title="Response body"
{
"id": "99890c34-e4b7-4a23-84d6-e5de57114c00", // [!code focus]
"name": "Partner Wallet App",
"openid4vpConfiguration": {
"authorizationEndpoint": "com.example.wallet://"
}
}
```
* `id` : We will use this value later to indicate this is the wallet the web application expects to
receive mDocs from.
2. [Start a presentation session](/docs/verification/remote-web-verifiers/tutorial#create-credential-request) and reference the wallet provider ID:
```typescript title="Verifier Web SDK example"
const options: MATTRVerifierSDK.RequestCredentialsOptions = {
credentialQuery: [credentialQuery],
challenge: MATTRVerifierSDK.utils.generateChallenge(),
openid4vpConfiguration: {
redirectUri: window.location.origin,
walletProviderId: "99890c34-e4b7-4a23-84d6-e5de57114c00"
},
};
const results = await MATTRVerifierSDK.requestCredentials(options);
```
3. The SDK will return a verification request URI and automatically render it as a QR code (cross-device flows) or deep link (same-device flows).
**Trade-offs:**
* ✅ Better targeting of a specific wallet application.
* ✅ Enables partnerships with specific wallet providers.
* ✅ Simple configuration on the verifier side.
* ⚠️ Requires coordination with the wallet provider to know their custom scheme.
* ⚠️ Users must have the specific wallet app installed.
* ⚠️ Other apps could potentially register the same scheme.
**Requirements for wallet applications:**
The wallet app must:
* Register to handle their custom URI scheme (e.g., `com.example.wallet://`).
* Support the OID4VP specification for processing verification requests.
* Handle the verification request URI and present credentials accordingly.
Because private-use schemes carry no implied protocol configuration, you must either know the wallet's supported capabilities out-of-band, or discover them dynamically via [wallet metadata](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-wallet-metadata-authorizati).
See the [holder guide](/docs/holding/remote-presentation-guides/handling-uri-schemes#private-use-uri-scheme) for implementation details.
#### Claimed HTTPS scheme (App Links / Universal Links) [#claimed-https-scheme-app-links--universal-links]
HTTPS schemes use domain-verified App Links (Android) or Universal Links (iOS) to direct verification requests to wallet applications that support these technologies. The wallet provider must configure their domain verification to enable this.
**When to use:**
* You want to work with wallet providers that support Universal/App Links.
* You're partnering with professional wallet providers that have domain-verified schemes.
* You want the smoothest user experience with no app selection prompts for users of those wallets.
**How it works:**
The wallet provider configures their domain with the necessary verification files and provides their HTTPS authorization endpoint. You configure this endpoint on your MATTR VII tenant. When you create a presentation session and reference that wallet provider, MATTR VII generates the verification request URI using the HTTPS scheme, which the operating system directs to the verified wallet app.
**Implementation:**
1. Obtain the wallet provider's HTTPS authorization endpoint. The wallet provider must have already set up domain verification files on their web server (see the [holder guide](/docs/holding/remote-presentation-guides/handling-uri-schemes#claimed-https-scheme-app-links--universal-links) for details on what wallet providers need to configure).
2. Create a supported wallet configuration with the wallet provider's HTTPS authorization endpoint. You can do this via the MATTR Portal or by making an API request:
1) Expand the **Credential Verification** section in the left-hand navigation panel.
2) Select **Supported wallets**.
3) Select the **Create new** button.
4) Use the *Name* text box to insert a meaningful and friendly name for your wallet application (for
example "Partner Wallet with Universal Links").
5) Use the *Authorize endpoint* text box to insert the private-use URI scheme, for example `https://wallet.example.com/verify`.
6) Select the **Create** button to create the new wallet configuration.
7) Note the wallet provider ID for this configuration, which you can use when creating presentation sessions to indicate that this is the wallet you expect to receive mDocs from.
Make the following request to your MATTR VII tenant to
[create a trusted wallet provider configuration](/docs/verification/remote-verification-api-reference/wallet-providers#create-a-wallet-provider):
```http title="Request"
POST /v2/presentations/wallet-providers
```
```json title="Request body"
{
"name": "Partner Wallet with Universal Links", // [!code focus]
"openid4vpConfiguration": {
"authorizationEndpoint": "https://wallet.example.com/verify" // [!code focus]
}
}
```
*Response*
```json title="Response body"
{
"id": "99890c34-e4b7-4a23-84d6-e5de57114c00", // [!code focus]
"name": "Partner Wallet with Universal Links",
"openid4vpConfiguration": {
"authorizationEndpoint": "https://wallet.example.com/verify"
}
}
```
* `id` : We will use this value later to indicate this is the wallet the web application expects to
receive mDocs from.
3. [Start a presentation session](/docs/verification/remote-web-verifiers/tutorial#create-credential-request) and reference the wallet provider ID:
```typescript title="Verifier Web SDK example"
const options: MATTRVerifierSDK.RequestCredentialsOptions = {
credentialQuery: [credentialQuery],
challenge: MATTRVerifierSDK.utils.generateChallenge(),
openid4vpConfiguration: {
redirectUri: window.location.origin,
walletProviderId: "99890c34-e4b7-4a23-84d6-e5de57114c00"
},
};
const results = await MATTRVerifierSDK.requestCredentials(options);
```
4. The SDK will return a verification request URI and automatically render it as a QR code (cross-device flows) or deep link (same-device flows).
**Trade-offs:**
* ✅ Smoothest user experience with no app selection prompts.
* ✅ Professional appearance with branded domain links.
* ✅ Works with reputable wallet providers that have domain verification.
* ✅ Operating system validates the domain association.
* ⚠️ Requires wallet providers to have set up domain verification.
* ⚠️ Users must have the specific wallet app installed.
* ⚠️ Requires coordination with wallet providers to obtain their authorization endpoint.
**Requirements for wallet applications:**
The wallet app must:
* Have configured Associated Domains (iOS) or intent filters with `autoVerify="true"` (Android).
* Host the necessary domain verification files on their web server.
* Handle HTTPS URLs from their domain.
* Support the OID4VP specification for processing verification requests.
See the [holder guide](/docs/holding/remote-presentation-guides/handling-uri-schemes#claimed-https-scheme-app-links--universal-links) for implementation details.
### Security considerations [#security-considerations]
When working with URI schemes for verification, it's important to understand the role they play in the overall security of your verification workflow:
**URI schemes and user experience:**
URI schemes primarily serve as 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 wallet app.
* Reduce confusion by limiting which apps are presented as options.
* For HTTPS schemes, leverage operating system domain verification to ensure a specific wallet app is the default handler.
**Verification security:**
The security of the verification workflow relies on the cryptographic verification of presented credentials, not on URI schemes. MATTR VII verifies:
* The credential's cryptographic signature using the trusted issuer's certificate.
* The credential's validity period and status.
* That the presented claims match what was requested.
* The device binding (for mDocs) to ensure the credential is being presented from the legitimate holder's device.
These cryptographic checks ensure that even if a different wallet application responds to the verification request, the credential itself must still be legitimate and cryptographically valid.
**Choosing wallet providers:**
While URI schemes don't enforce which wallet can respond at a protocol level, they enable you to:
* Direct users to specific wallet applications through targeted URI schemes.
* Build business relationships with specific wallet providers.
* Provide a better user experience by reducing ambiguity about which app to use.
For stronger guarantees about which wallets can interact with your verification system, consider additional mechanisms such as wallet attestation or business agreements with wallet providers.
For questions about advanced security configurations, please [contact us](mailto:dev-support@mattr.global).
# Verifier Web SDK
URL: /docs/verification/remote-web-verifiers/sdks/overview
The Verifier Web SDK is a powerful tool for integrating online credential verification capabilities
into your web applications. It enables secure and efficient verification of [mDocs](/docs/concepts/mdocs),
supporting both same-device and cross-device verification workflows. The SDK leverages the MATTR VII platform to handle credential presentation and verification processes.
## SDK Capabilities [#sdk-capabilities]
* Handle the verification of an mDoc via a
[remote (online) presentation workflow](/docs/verification/remote-overview) as per
[ISO/IEC 18013-7:2025](https://www.iso.org/standard/91154.html) and
[OID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html).
* Support for both same-device and cross-device verification workflows.
* Simple integration into web applications.
* Secure handling of mDoc requests and responses.
To get started with this SDK, please [contact us](mailto:sales@mattr.global)
so we can work together to find the best solution for you.
## Versions [#versions]
Below are the available versions of the mDocs Verifier Web SDK, including the current active
version, supported versions, and those that have reached end-of-life (EOL).
| Major version | Status | Latest release | End of Life date | Documentation |
| ------------- | ------ | -------------- | ---------------- | ------------------------------------------------------------------------------------ |
| v2 | Active | 2.2.0 | - | [SDK Docs](https://api-reference-sdk.mattr.global/verifier-sdk-web/2.2.0/index.html) |
| v1 | EOL | 1.1.0 | 29-08-2025 | - |
# Embedded Web App integration pattern
URL: /docs/verification/remote-web-verifiers/idv-integration-patterns/embedded
mDoc/mDL acceptance is typically done as part of a wider flow that includes other checks such as document scanning, facial recognition and liveness checks.
Many of these standard identity verification checks are best performed on a mobile device due to the availability of built-in hardware like cameras and biometric sensors. Embedding the Verifier Web SDK capabilities within an existing IDV web application allows users to complete these verification steps seamlessly on their mobile devices while interacting with a single web application.
If a user starts within a desktop browser, the assumption is that the IDV web application will securely transfer the active session to their mobile device (for example, by displaying a QR code that the user scans with their mobile browser). This ensures session continuity, allowing the user to continue the verification process on their mobile device without restarting the flow.
## Overview [#overview]
* **Verification channel**: Remote, Unsupervised
* **Device/s**: Same-device
* **Formats**: mDocs
* **Information assurance level**: Very High
* **Identity assurance level**: High (exact identity assurance levels depend on specific IDV blocks
implemented in the workflow).
## Journey pattern [#journey-pattern]
## Architecture [#architecture]
The following architecture flow assumes the user is now interacting with the IDV web application on their mobile device. They either started the session on mobile, or were transferred there from a desktop browser via a secure session handoff.
### Pass Challenge [#pass-challenge]
The IDV back-end server generates a unique challenge related to the specific session and passes it to the IDV web app.
### Start Presentation Session [#start-presentation-session]
The IDV web app uses the MATTR Verifier Web SDK to start a presentation session and convert the credential query into a signed credential request.
### Presentation Request [#presentation-request]
The Verifier Web SDK passes the presentation request via the web browser to the Digital Credentials API, which surfaces matching credentials and wallets to the user.
### Presentation Response [#presentation-response]
The user consents to share the requested credential data, which is returned to the Verifier Web SDK which then passes it onto the MATTR VII verifier tenant for verification.
### Credential Verification [#credential-verification]
The MATTR VII verifier tenant verifies the credential presentation, including checking its digital signature against a list of trusted issuers.
Once verification is completed, the verifier tenant informs the Verifier Web SDK that verification results are available.
### Fetch Verified Data [#fetch-verified-data]
The IDV backend server uses the session ID and challenge to securely fetch the verified data from the MATTR VII tenant.
The IDV backend can then pass verification results to the IDV web app, and continue the journey as needed.
## Detailed Journey flow [#detailed-journey-flow]
The following journey begins when the options for the user to verify using an mDL is surfaced via the IDV application:
1. The IDV Web App initializes the Verifier Web SDK to prepare for credential verification.
2. As part of the IDV verification workflow, the user selects to verify their identity using an mDL.
3. The IDV Web App requests a unique challenge from the IDV Backend. This challenge will be used later to correlate the verification session.
4. The IDV Backend generates and returns a unique challenge to the IDV Web App.
5. The IDV Web App instructs the Verifier Web SDK to request credentials from the user.
6. The Verifier Web SDK requests the MATTR VII Verifier Tenant to create a new presentation session, using the unique challenge.
7. The MATTR VII Verifier Tenant generates and returns a presentation request to the Verifier Web SDK.
8. The Verifier Web SDK uses the presentation request to invoke the browser's Digital Credentials API to surface available credentials.
9. The Digital Credentials API presents matching credentials to the user.
10. The user selects which mDL credential they wish to share.
11. The user's wallet displays the detailed request information and asks for consent.
12. The user reviews the request and provides explicit consent to share the requested information from their credential.
13. The user's wallet sends the encrypted credential response to the Verifier Web SDK.
14. The Verifier Web SDK forwards the credential response to the MATTR VII Verifier Tenant.
15. The MATTR VII Verifier Tenant decrypts the response and verifies the authenticity of the credential, including checks such as signature validation, revocation status and the identity of the issuer.
16. The MATTR VII Verifier Tenant returns a session ID to the Verifier Web SDK.
17. The Verifier Web SDK informs the IDV Web App that verification results are ready.
18. The IDV Web App requests the verification results from the IDV Backend.
19. The IDV Backend retrieves the results from the MATTR VII Verifier Tenant using the session ID and challenge. This request is made via a secure server-to-server connection.
20. The MATTR VII Verifier Tenant returns the verification results to the IDV Backend.
21. The IDV Backend forwards the verification results to the IDV Web App. The IDV Web App can now correlate these results with the original challenge to ensure integrity.
22. The IDV Web App allows the user to proceed with any additional verification steps or complete the verification flow.
If the user started on a different device, the assumption is that the IDV web app will inform the original browser session so the user can pick-up where they started.
# Login IDV integration pattern
URL: /docs/verification/remote-web-verifiers/idv-integration-patterns/login
This integration pattern is used to verify a credential presented
[remotely](/docs/verification/remote-web-verifiers/workflow) when attempting to log into an
existing service from a different device.
## Overview [#overview]
* **Verification channel**: Remote, Unsupervised
* **Device/s**: Cross-device
* **Formats**: mDocs
* **Information assurance level**: Very High
* **Identity assurance level**: High.
## Journey flow [#journey-flow]
## Architecture [#architecture]
### Interacting with the website [#interacting-with-the-website]
The user is using a web browser on their desktop to access a website where they attempt to log into
a service where they already have an active account. They are logged into this service on their
mobile device.
### Requesting an mDL for verification [#requesting-an-mdl-for-verification]
One of the options to login on their desktop browser is to present an mDL for verification. This mDL
will then be compared with information stored as part of the initial account setup.
This is achieved by embedding the MATTR Pi Verifier Web SDK into the web application and requesting
the user to display an mDL for verification.
When the user agrees to proceed, the Verifier Web SDK makes a request to a configured MATTR VII
Verifier tenant. That request defines what credentials and claims are required for verification.
The MATTR VII verifier tenant is configured with the following:
* What domains it can accept requests from.
* What workflows it supports (e.g. same-device and/or cross-device).
* What wallet applications it can interact with.
* How to invoke these supported wallet applications.
The MATTR VII verifier tenant recognizes that the user began the interaction on a desktop browser
and responds with a link that is rendered as a QR code by the Verifier Web SDK inside the web
application.
The user then scans that QR code with a mobile device to invoke a matching native application which
includes the required mDL.
### Presenting request details to the user [#presenting-request-details-to-the-user]
Once the wallet is launched, it authenticates the user and interacts with the MATTR VII tenant to
retrieve and display the request details to the user:
* What credentials are requested.
* What claims from the credentials are requested.
* Whether the relying party is vetted by the digital trust service, and whether they are allowed to
request this type of information.
* What matching credentials are available and can be shared with the verifier.
Based on that information, the user can select to proceed with the verification workflow and share
the required information with the verifier.
### Verifying the mDL [#verifying-the-mdl]
The MATTR VII verifier tenant verifies the shared credentials to validate that:
* The information has not been tampered with.
* The credential has not been revoked (or suspended, for legacy credentials).
* The credential has not expired.
* The credential was issued by a trusted issuer (based on information retrieved from the DTS).
### Displaying verification results [#displaying-verification-results]
The MATTR VII verifier tenant shares the verification results with the Verifier Web SDK. These
results can then be compared to existing databases to verify that this is the same user who created
the account.
Upon successful verification, the user is logged into their account on their desktop browser.
# IDV Onboarding integration pattern
URL: /docs/verification/remote-web-verifiers/idv-integration-patterns/onboarding
This integration pattern is used to verify a credential presented
[remotely](/docs/verification/remote-web-verifiers/workflow) as part of onboarding a customer to a
new service via either a [cross-device](#cross-device-flow) or [same-device](#same-device-flow) flow.
## Cross-device flow [#cross-device-flow]
### Overview [#overview]
* **Verification channel**: Remote, unsupervised
* **Device/s**: Cross-device (Mobile app + desktop).
* **Formats**: mDocs
* **Information assurance level**: Very High
* **Identity assurance level**: High (exact identity assurance levels depends on specific IDV blocks
implemented in the workflow).
### Integration pattern [#integration-pattern]
### Architecture [#architecture]
#### Interacting with the website [#interacting-with-the-website]
The user is using a web browser on their desktop to access a website where they attempt to create a
new account. This requires them to complete an IDV workflow.
#### Requesting a credential for verification [#requesting-a-credential-for-verification]
The first step in the IDV workflow is to present an mDL for verification.
This is achieved by embedding the MATTR Pi Verifier Web SDK into the web application and the IDV
workflow by requesting the user to display an mDL for verification.
When the user agrees to proceed, the Verifier Web SDK makes a request to a configured MATTR VII
Verifier tenant. That request defines what credentials and claims are required for verification.
The MATTR VII verifier tenant is configured with the following:
* What domains it can accept requests from.
* What workflows it supports (e.g. same-device and/or cross-device).
* What wallet applications it can interact with.
* How to invoke these supported wallet applications.
The MATTR VII verifier tenant recognizes that the user began the interaction on a desktop browser
and responds with a link that is rendered as a QR code by the Verifier Web SDK inside the web
application.
The user then scans that QR code with a mobile device to invoke a matching native application which
includes the required mDL.
#### Presenting request details to the user [#presenting-request-details-to-the-user]
Once the wallet is launched, it authenticates the user and interacts with the MATTR VII tenant to
retrieve and display the request details to the user:
* What credentials are requested.
* What claims from the credentials are requested.
* Whether the relying party is vetted by the digital trust service, and whether they are allowed to
request this type of information.
* What matching credentials are available and can be shared with the verifier.
Based on that information, the user can select to proceed with the verification workflow and share
the required information with the verifier.
#### Verifying the mDL [#verifying-the-mdl]
The MATTR VII verifier tenant verifies the shared credentials to validate that:
* The information has not been tampered with.
* The credential has not been revoked (or suspended, for legacy credentials).
* The credential has not expired.
* The credential was issued by a trusted issuer.
#### Handling verification results [#handling-verification-results]
The MATTR VII verifier tenant shares the verification results with the Verifier Web SDK. These
results are then displayed to the user on their desktop browser, allowing them to continue the IDV
workflow and complete any additional required steps.
The IDV workflow can use information retrieved from the verified mDL (e.g. portrait image,
credential claims) and compare them against any existing databases to increase the workflows’
assurance levels.
This is just an example workflow. The mDL verification can be embedded in any part of the IDV
journey to accommodate different workflows and requirements.
## Same-device flow [#same-device-flow]
### Overview [#overview-1]
* **Issuance channel**: Remote, unsupervised
* **Device/s**: Same-device (Web app to mobile app, Mobile app to mobile app).
* **Formats**: mDocs
* **Information assurance level**: Very High
* **Identity assurance level**: High (exact identity assurance levels depends on specific IDV blocks
implemented in the workflow).
### Integration pattern [#integration-pattern-1]
#### Opening an account on a mobile device [#opening-an-account-on-a-mobile-device]
Samantha starts the process of creating an account with a new service that demands high assurance
levels (such as opening a bank account) - either in a mobile browser or a native mobile application.
#### Request for identity document [#request-for-identity-document]
Samantha is prompted to provide an identity document, such as a Mobile Driver's License (mDL),
another verifiable mobile credential (mDoc), a physical document scan, or an alternative ID
verification option. She decides to submit her mDL.
#### Invoking a digital wallet [#invoking-a-digital-wallet]
Samantha is redirected into a mobile application (e.g. wallet) holding the matching mDL.
#### Sharing credentials [#sharing-credentials]
Samantha is presented with a summary of the information from her mDL that will be shared during this
process. She is then required to provide her consent and complete device authentication before
proceeding with the data sharing.
#### Redirect back to the website [#redirect-back-to-the-website]
Samantha is redirected from the mobile application/wallet back to the mobile browser or original
native mobile application, allowing her to continue with the interaction.
#### Additional IDV journey blocks [#additional-idv-journey-blocks]
Once her mDL is successfully verified, Samantha can proceed with the next steps in the IDV process.
These may include different verification methods, such as:
* Biometric check.
* Liveness detection.
* Call center validation.
* Proof of address check.
* Voice capture.
* Database check.
* Live video interview.
* Authentication.
* Physical ID validation.
#### Continue with opening account [#continue-with-opening-account]
Once the IDV process is complete, Samantha can move forward with opening her account. Some fields
may be pre-populated with information gathered during the IDV journey, including details from the
mDL verification.
#### Account opened [#account-opened]
Samantha successfully opens a new account with the service.
### Architecture [#architecture-1]
#### Interacting with the website [#interacting-with-the-website-1]
The user is using a web browser on their mobile device to access a website where they attempt to
create a new account. This requires them to complete an IDV workflow.
#### Requesting an mDL for verification [#requesting-an-mdl-for-verification]
The first step in the IDV workflow is to present an mDL for verification.
This is achieved by embedding the MATTR Pi Verifier Web SDK into the web application and the IDV
workflow by requesting the user to display an mDL for verification.
When the user agrees to proceed, the Verifier Web SDK makes a request to a configured MATTR VII
Verifier tenant. That request defines what credentials and claims are required for verification.
The MATTR VII verifier tenant is configured with the following:
* What domains it can accept requests from.
* What workflows it supports (e.g. same-device and/or cross-device).
* What wallet applications it can interact with.
* How to invoke these supported wallet applications.
The MATTR VII verifier tenant recognizes that the user began the interaction on a mobile browser and
responds with a link that is used by the Verifier Web SDK to redirect the user from their web
browser and invoke a matching native application installed on the same mobile device and which holds
the required mDL.
#### Presenting request details to the user [#presenting-request-details-to-the-user-1]
Once the wallet is launched, it authenticates the user and interacts with the MATTR VII tenant to
retrieve and display the request details to the user:
* What credentials are requested.
* What claims from the credentials are requested.
* Whether the relying party is vetted by the digital trust service, and whether they are allowed to
request this type of information.
* What matching credentials are available and can be shared with the verifier.
Based on that information, the user can select to proceed with the verification workflow and share
the required information.
#### Verifying the mDL [#verifying-the-mdl-1]
The MATTR VII verifier tenant verifies the shared credentials to validate that:
* The information has not been tampered with.
* The credential has not been revoked (or suspended, for legacy credentials).
* The credential has not expired.
* The credential was issued by a trusted issuer.
#### Handling verification results [#handling-verification-results-1]
The MATTR VII verifier tenant shares the verification results with the Verifier Web SDK, which
redirects the user back to their browser where they can continue the IDV workflow and complete any
additional required steps.
The IDV workflow can use information retrieved from the verified mDL (e.g. portrait image,
credential claims) and compare them against any existing databases to increase the workflows’
assurance levels.
This is just an example workflow. The mDL verification can be embedded in any part of the IDV
journey to accommodate different workflows and requirements.
# Retrieve custom domain
## Endpoint
```
GET /v1/config/domain
```
Full URL: `https://example.vii.au01.mattr.global/v1/config/domain`
### Authorization
Bearer token required.
Required roles: admin, issuer, verifier, dts-provider, managed-issuer
## Description
Returns your tenant's custom domain configuration and its verification status.
### **Analytic events**
* CONFIG_CUSTOM_DOMAIN_RETRIEVE_START
* CONFIG_CUSTOM_DOMAIN_RETRIEVE_SUCCESS
* CONFIG_CUSTOM_DOMAIN_RETRIEVE_FAIL
### Responses
#### 200 - Custom domain returned
```json
{
"name": "Example Corp",
"logoUrl": "https://cdn.example.com/logo.icon",
"domain": "example.com",
"verificationToken": "8c6f36c1-91ff-439d-a518-48cf7ef421ef",
"isVerified": false
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Configure custom domain
## Endpoint
```
POST /v1/config/domain
```
Full URL: `https://example.vii.au01.mattr.global/v1/config/domain`
### Authorization
Bearer token required.
Required roles: admin, issuer, verifier, dts-provider, managed-issuer
## Description
Creates a custom domain configuration on your tenant. You can configure a custom domain for a specific MATTR VII tenant to represent your brand and instil trust with your end-users. Any MATTR VII tenant can only have one custom domain. Refer to our [docs](https://learn.mattr.global/docs/platform-management/custom-domain-overview) for more information.
### **Analytic events**
* CONFIG_CUSTOM_DOMAIN_CREATE_START
* CONFIG_CUSTOM_DOMAIN_CREATE_SUCCESS
* CONFIG_CUSTOM_DOMAIN_CREATE_FAIL
### Request Body
The custom domain payload
### Responses
#### 201 - Custom domain created
```json
{
"name": "Example Corp",
"logoUrl": "https://cdn.example.com/logo.icon",
"domain": "example.com",
"verificationToken": "8c6f36c1-91ff-439d-a518-48cf7ef421ef",
"isVerified": false
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete custom domain
## Endpoint
```
DELETE /v1/config/domain
```
Full URL: `https://example.vii.au01.mattr.global/v1/config/domain`
### Authorization
Bearer token required.
Required roles: admin, issuer, verifier, dts-provider, managed-issuer
## Description
Deletes the custom domain configuration on your tenant.
Deleting your custom domain configuration breaks the linkage with any credentials issued under the custom domain. These credentials will no longer be valid.
### **Analytic events**
* CONFIG_CUSTOM_DOMAIN_DELETE_START
* CONFIG_CUSTOM_DOMAIN_DELETE_SUCCESS
* CONFIG_CUSTOM_DOMAIN_DELETE_FAIL
### Responses
#### 204 - Custom domain deleted
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update custom domain
## Endpoint
```
PUT /v1/config/domain
```
Full URL: `https://example.vii.au01.mattr.global/v1/config/domain`
### Authorization
Bearer token required.
Required roles: admin, issuer, verifier, dts-provider, managed-issuer
## Description
Updates the custom domain configuration.
### **Analytic events**
* CONFIG_CUSTOM_DOMAIN_UPDATE_START
* CONFIG_CUSTOM_DOMAIN_UPDATE_SUCCESS
* CONFIG_CUSTOM_DOMAIN_UPDATE_FAIL
### Request Body
### Responses
#### 200 - Custom Domain updated
```json
{
"name": "Example Corp",
"logoUrl": "https://cdn.example.com/logo.icon",
"domain": "example.com",
"verificationToken": "8c6f36c1-91ff-439d-a518-48cf7ef421ef",
"isVerified": false
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Verify custom domain
## Endpoint
```
POST /v1/config/domain/verify
```
Full URL: `https://example.vii.au01.mattr.global/v1/config/domain/verify`
### Authorization
Bearer token required.
Required roles: admin, issuer, verifier, dts-provider, managed-issuer
## Description
Verifies that you have control of the configured custom domain by examining its TXT record.
Your custom domain will not be active until you verify it. Refer to [Verify domain ownership](https://learn.mattr.global/docs/platform-management/custom-domain-overview#verify-domain-ownership) for more information.
### **Analytic events**
* CONFIG_CUSTOM_DOMAIN_VERIFY_START
* CONFIG_CUSTOM_DOMAIN_VERIFY_SUCCESS
* CONFIG_CUSTOM_DOMAIN_VERIFY_FAIL
### Responses
#### 204 - Custom domain verified
#### 400 - Bad Request
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Well known DID configuration
## Endpoint
```
GET /.well-known/did-configuration
```
Full URL: `https://example.vii.au01.mattr.global/.well-known/did-configuration`
### Authorization
None required.
## Description
Returns a list of Decentralized Identifier (DID) Configuration entries from the tenant. These are automatically created for **all** DIDS created on a tenant so that they can be used by any party aiming to establish and verify the domain-DID linkage by exposing cryptographic proofs. Thus, this endpoint is unprotected, public facing and can be deterministically found at the root of the tenant subdomain or alias by any party. Refer to [Well Known DID Configuration](https://identity.foundation/.well-known/resources/did-configuration) on the Decentralized Identity Foundation website for more information.
### Responses
#### 200 - List of DID Configuration entries
```json
{
"entries": [
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"type": [
"VerifiableCredential",
"AlumniCredential"
],
"credentialBranding": {
"backgroundColor": "#B00AA0",
"watermarkImageUrl": "https://example.edu/img/watermark.png"
},
"issuanceDate": "2020-05-02T12:06:29.156Z",
"credentialStatus": {
"id": "https://tenant.vii.mattr.global/v1/revocation-lists/cc641396-3750-43c8-b8b8-f30d74eb3fb3#1",
"type": "RevocationList2020Status",
"revocationListIndex": 1,
"revocationListCredential": "https://tenant.vii.mattr.global/v1/revocation-lists/cc641396-3750-43c8-b8b8-f30d74eb3fb3"
},
"credentialSubject": {
"givenName": "Jamie",
"familyName": "Doe",
"alumniOf": "Example University"
},
"proof": {
"type": "Ed25519Signature2018",
"created": "2020-05-02T12:06:29Z",
"jws": "EXAMPLE_JWS_TOKEN_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
"proofPurpose": "assertionMethod",
"verificationMethod": "did:web:organization.com"
},
"name": "Alumni Credential",
"description": "This credential shows that the person has attended the mentioned university."
}
]
}
```
# Retrieve a list of DIDs
## Endpoint
```
GET /v1/dids
```
Full URL: `https://example.vii.au01.mattr.global/v1/dids`
### Authorization
Bearer token required.
Required roles: admin, issuer, verifier, dts-provider, managed-issuer
## Description
Returns a list of all DIDs (Decentralized Identifiers) managed by the tenant and their associated meta-data.
### **Analytic events**
* DID_RETRIEVE_LIST_START
* DID_RETRIEVE_LIST_SUCCESS
* DID_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - A list of DIDs
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM",
"data": [
{
"did": "did:key:z6Mkt7bFYc4V2HdAxwhMtaY6cgJckYXwhYdPLJCcnVqzrkpr",
"localMetadata": {
"registered": 1583233799656,
"keys": [
{
"kmsKeyId": "ad8facc7-e7f6-4af6-9baa-2f7abd71c928",
"didDocumentKeyId": "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH#z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH"
}
]
}
}
]
}
```
# Create a DID
## Endpoint
```
POST /v1/dids
```
Full URL: `https://example.vii.au01.mattr.global/v1/dids`
### Authorization
Bearer token required.
Required roles: admin, issuer, verifier, dts-provider, managed-issuer
## Description
Takes a supported [DID method](https://learn.mattr.global/docs/concepts/dids#methods) and returns a new DID with its generated keys and required information. This endpoint also registers the DID Document when applicable.
MATTR VII currently supports creating DIDs of the following methods:
- **did:key**: The most basic type of DID. The public key forms the DID and has no further data associated with it.
- **did:web**: This type of DID requires hosting the DID document on a publicly accessible domain in order to make the document and its contents available.
### **Analytic events**
* DID_CREATE_START
* DID_CREATE_SUCCESS
* DID_CREATE_FAIL
### Request Body
Options for creating the decentralized identifier
```json
{
"options": {
"keyType": "Bls12381G2",
"url": "learn.vii.au01.mattr.global"
}
}
```
### Responses
#### 201 - DID document created
```json
{
"registrationStatus": "COMPLETED",
"did": "did:key:z6Mkt7bFYc4V2HdAxwhMtaY6cgJckYXwhYdPLJCcnVqzrkpr",
"metadata": {
"registered": 1583233799656,
"keys": [
{
"kmsKeyId": "ad8facc7-e7f6-4af6-9baa-2f7abd71c928",
"didDocumentKeyId": "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH#z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH"
}
]
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Resolve a DID
## Endpoint
```
GET /v1/dids/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v1/dids/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, verifier, dts-provider, managed-issuer
## Description
Retrieves a DID and its metadata from the tenant by its URI. This may involve a network call depending on the method involved:
- For did:key the public key is encapsulated in the DID URI itself.
- For did:web it must be resolved by accessing the `/.well-known/did.json` path on its domain.
### **Analytic events**
* DID_RETRIEVE_START
* DID_RETRIEVE_SUCCESS
* DID_RETRIEVE_FAIL
### Responses
#### 200 - A DID Document and its meta-data
```json
{
"localMetadata": {
"registered": 1583233799656,
"keys": [
{
"kmsKeyId": "ad8facc7-e7f6-4af6-9baa-2f7abd71c928",
"didDocumentKeyId": "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH#z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH"
}
]
}
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a DID
## Endpoint
```
DELETE /v1/dids/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v1/dids/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, verifier, dts-provider, managed-issuer
## Description
Deletes a DID and all associated metadata by providing its URI. This includes all the removal of all associated private keys from the Key Management System (KMS).
For `did:web` you will need to manually remove the `did.json` from your hosted domain.
### **Analytic events**
* DID_DELETE_START
* DID_DELETE_SUCCESS
* DID_DELETE_FAIL
### Responses
#### 204 - DID successfully deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Sign a CWT credential
## Endpoint
```
POST /v2/credentials/compact/sign
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/sign`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns a signed CWT credential generated from a provided valid payload.
The payload can include any number of custom claims, as CWT credentials do not comply with any specific standard or specification.
### **Analytic events**
* CREDENTIAL_COMPACT_SIGN_START
* CREDENTIAL_COMPACT_SIGN_SUCCESS
* CREDENTIAL_COMPACT_SIGN_FAIL
### Request Body
CWT credential payload to sign
```json
{
"payload": {
"iss": "did:web:organization.com",
"nbf": 1645743759,
"exp": 1646743759,
"iat": 1645743759
}
}
```
### Responses
#### 200 - CWT credential signed
```json
{
"encoded": "CSC:/1/2KCE3IQEJB5DCMSLN5KWKZABE2QFQRVDAF4CIZDJMQ5HOZLCHIYDGOJUFUYTENJNGIZTOLJVGIWTCMJQFZXGO4TPNMXGS33ENZQW2ZLEJJXWQ3QH3BAFB3LISHKGQ2KBJ6Q35NXZFD6LGZ2YIAYHZAKCF7NKTIUZUTZQ3PWDBALAWVRG5XL2H4P4WFK25X3Y5X5RTN7NOZUST67KLCEFS3EPXQU5KM7VUGOPXJLQ6K5U676PMQNWRZCZ",
"decoded": {
"iss": "did:web:organization.com",
"nbf": 1645743759,
"exp": 1646743759,
"iat": 1645743759,
"jti": "6tVMmKodQNaLywW6NGA2aA",
"type": "CredentialType",
"property1": "...",
"property2": "..."
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Format a CWT credential as a QR code
## Endpoint
```
POST /v2/credentials/compact/qrcode
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/qrcode`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns a QR code representation of a CWT credential from a provided encoded string representation of that credential.
### **Analytic events**
* CREDENTIAL_COMPACT_QRCODE_CREATE_START
* CREDENTIAL_COMPACT_QRCODE_CREATE_SUCCESS
* CREDENTIAL_COMPACT_QRCODE_CREATE_FAIL
### Request Body
```json
{
"payload": "CSS:/1/2KCE3IQEJB5DCMSMGRKXI3IBE2QFSANKVACBUYQYB2HQKGTCDAHI6BQ2MIMA5DYBPAUWI2L...",
"width": 250
}
```
### Responses
#### 200 - QR code generated
#### 400 - Bad Request
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Format a CWT credential as a PDF
## Endpoint
```
POST /v2/credentials/compact/pdf
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/pdf`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns a PDF representation of a provided CWT credential based on an existing PDF template.
The request will fail if the provided credential isn't valid or has expired.
### **Analytic events**
* CREDENTIAL_COMPACT_PDF_CREATE_START
* CREDENTIAL_COMPACT_PDF_CREATE_SUCCESS
* CREDENTIAL_COMPACT_PDF_CREATE_FAIL
### Request Body
Credential payload
```json
{
"templateId": "4eea7654-d4c5-4eba-bd7a-5ca334d54725",
"payload": "{payload}"
}
```
### Responses
#### 200 - PDF created
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Format a CWT credential as an Apple Pass
## Endpoint
```
POST /v2/credentials/compact/digital-pass/apple
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/digital-pass/apple`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns an Apple Pass representation of a provided CWT credential based on an existing Apple Pass template.
The request will fail if the provided credential isn't valid or has expired.
### **Analytic events**
* CREDENTIAL_COMPACT_APPLE_PASS_CREATE_START
* CREDENTIAL_COMPACT_APPLE_PASS_CREATE_SUCCESS
* CREDENTIAL_COMPACT_APPLE_PASS_CREATE_FAIL
### Request Body
```json
{
"templateId": "3812166c-ac9f-4e4e-96dd-c1336b5be378",
"payload": "{payload}"
}
```
### Responses
#### 200 - Apple Pass created
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Format a CWT credential as a Google Pass
## Endpoint
```
POST /v2/credentials/compact/digital-pass/google
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/digital-pass/google`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns a Google Pass representation of a provided CWT credential based on an existing Google Pass template.
The request will fail if the provided credential isn't valid or has expired.
### **Analytic events**
* CREDENTIAL_COMPACT_GOOGLE_PASS_CREATE_START
* CREDENTIAL_COMPACT_GOOGLE_PASS_CREATE_SUCCESS
* CREDENTIAL_COMPACT_GOOGLE_PASS_CREATE_FAIL
### Request Body
```json
{
"templateId": "3812166c-ac9f-4e4e-96dd-c1336b5be378",
"payload": "{payload}"
}
```
### Responses
#### 200 - Google Pass created
```json
{
"redirectTo": "https://pay.google.com/gp/v/save/{jwt}"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete CWT credential metadata
## Endpoint
```
DELETE /v2/credentials/compact/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Deletes all credential metadata from the tenant for a specific credential by providing its ID. If the credential was set to be revocable, it will be permanently revoked upon metadata deletion.
Note that only metadata of revocable credentials or credentials issued via the OID4VCI flow is saved.
Deleted metadata cannot be recovered.
### Responses
#### 204 - Credential metadata deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all CWT credential revocation lists
## Endpoint
```
GET /v2/credentials/compact/revocation-lists
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/revocation-lists`
### Authorization
None required.
## Description
Returns a list of all CWT credential revocation lists on the tenant.
This endpoint is intended for public consumption, and as such does not require authentication.
### **Analytic events**
* CREDENTIAL_COMPACT_REVOCATION_LISTS_RETRIEVE_START
* CREDENTIAL_COMPACT_REVOCATION_LISTS_RETRIEVE_SUCCESS
* CREDENTIAL_COMPACT_REVOCATION_LISTS_RETRIEVE_FAIL
### Responses
#### 200 - Revocation lists retrieved
# Retrieve CWT credential revocation list
## Endpoint
```
GET /v2/credentials/compact/revocation-lists/{listId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/revocation-lists/{listId}`
### Authorization
None required.
## Description
Returns a CWT credential revocation list by providing its ID.
This endpoint is intended for public consumption, and as such does not require authentication.
### **Analytic events**
* CREDENTIAL_COMPACT_REVOCATION_RETRIEVE_START
* CREDENTIAL_COMPACT_REVOCATION_RETRIEVE_SUCCESS
* CREDENTIAL_COMPACT_REVOCATION_RETRIEVE_FAIL
### Responses
#### 200 - Revocation list retrieved
# Retrieve CWT credential revocation status
## Endpoint
```
GET /v2/credentials/compact/{id}/revocation-status
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/{id}/revocation-status`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieve the revocation status of a CWT credential by providing its ID.
### Responses
#### 200 - Revocation status retrieved
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update CWT credential revocation status
## Endpoint
```
POST /v2/credentials/compact/{id}/revocation-status
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/{id}/revocation-status`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Updates the credential status as revoked (invalid) or unrevoked (valid).
### **Analytic events**
* CREDENTIAL_COMPACT_REVOCATION_SET_STATUS_START
* CREDENTIAL_COMPACT_REVOCATION_SET_STATUS_SUCCESS
* CREDENTIAL_COMPACT_REVOCATION_SET_STATUS_FAIL
### Request Body
Update revocation status
### Responses
#### 200 - Revocation status updated
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Verify a CWT credential
## Endpoint
```
POST /v2/credentials/compact/verify
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/verify`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Verify a CWT credential by providing the encoded payload and specifying verification options.
You can provide a valid CWT credential as either an encoded string or a QR code in a PDF document or an image file.
Standard checks performed on all verification requests:
- Conformance of the string and encoded data. All string representations of CWT credentials must be prefixed with `CSC/1`.
- Decoded payload structure is a valid CWT credential.
- Issuer DID can be used to resolve its `did.json` document.
- Public key from issuer's `did.json` document validates the proof signature, confirming the credential has not been tampered with.
Optional parameter checks:
- Credential was issued by a trusted issuer.
- Current time is after the beginning of the credential validity period.
- Current time is not after the end of the credential validity period.
- Credential has not been revoked.
### **Analytic events**
* CREDENTIAL_COMPACT_VERIFY_START
* CREDENTIAL_COMPACT_VERIFY_SUCCESS
* CREDENTIAL_COMPACT_VERIFY_FAIL
### Request Body
```json
{
"payload": "CSC:/1/2KCE3IQEJB5DCMSLN5KWKZABE2QFQRVDAF4CIZDJMQ5HOZLCHIYDGOJUFUYTENJNGIZTOLJVGIWTCMJQFZXGO4TPNMXGS33ENZQW2ZLEJJXWQ3QH3BAFB3LISHKGQ2KBJ6Q35NXZFD6LGZ2YIAYHZAKCF7NKTIUZUTZQ3PWDBALAWVRG5XL2H4P4WFK25X3Y5X5RTN7NOZUST67KLCEFS3EPXQU5KM7VUGOPXJLQ6K5U676PMQNWRZCZ",
"trustedIssuers": [
"did:web:organization.com"
]
}
```
### Responses
#### 200 - Verification completed
```json
{
"decoded": {
"iss": "did:web:organization.com",
"nbf": 1645743759,
"exp": 1646743759,
"iat": 1645743759,
"jti": "6tVMmKodQNaLywW6NGA2aA",
"type": "CredentialType",
"property1": "...",
"property2": "..."
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 413 - Payload Too Large
#### 415 - Unsupported Media Type
# Retrieve all CWT credential configurations
## Endpoint
```
GET /v2/credentials/compact/configurations
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/configurations`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns a list of all CWT credential configurations from your tenant.
### **Analytic events**
* CREDENTIAL_COMPACT_CREDENTIAL_CONFIGURATION_RETRIEVE_LIST_START
* CREDENTIAL_COMPACT_CREDENTIAL_CONFIGURATION_RETRIEVE_LIST_SUCCESS
* CREDENTIAL_COMPACT_CREDENTIAL_CONFIGURATION_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
- `type`: string
The optional credential type to filter on
### Responses
#### 200 - CWT credential configurations retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM",
"data": [
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"type": "CourseCredential",
"claimMappings": {
"firstName": {
"mapFrom": "claims.given_name",
"required": true
},
"address": {
"mapFrom": "claims.address.formatted"
},
"picture": {
"mapFrom": "claims.picture",
"defaultValue": "http://example.edu/img/placeholder.png"
},
"badge": {
"defaultValue": "http://example.edu/img/badge.png"
},
"providerSubjectId": {
"mapFrom": "authenticationProvider.subjectId"
}
},
"expiresIn": {
"years": 1,
"months": 12,
"weeks": 52,
"days": 365,
"hours": 24,
"minutes": 1440,
"seconds": 3600
}
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create a CWT credential configuration
## Endpoint
```
POST /v2/credentials/compact/configurations
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/configurations`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Creates a new CWT credential configuration, a specific set of rules and parameters that are used to create and validate a particular type of verifiable credential. These rules and parameters define how the credential is structured and what data it contains when issued.
### **Analytic events**
* CREDENTIAL_COMPACT_CREDENTIAL_CONFIGURATION_CREATE_START
* CREDENTIAL_COMPACT_CREDENTIAL_CONFIGURATION_CREATE_SUCCESS
* CREDENTIAL_COMPACT_CREDENTIAL_CONFIGURATION_CREATE_FAIL
### Request Body
The credential configuration payload
```json
{
"type": "CourseCredential",
"claimMappings": {
"firstName": {
"mapFrom": "claims.given_name",
"required": true
},
"address": {
"mapFrom": "claims.address.formatted"
},
"picture": {
"mapFrom": "claims.picture",
"defaultValue": "http://example.edu/img/placeholder.png"
},
"badge": {
"defaultValue": "http://example.edu/img/badge.png"
},
"providerSubjectId": {
"mapFrom": "authenticationProvider.subjectId"
}
},
"expiresIn": {
"years": 1,
"months": 12,
"weeks": 52,
"days": 365,
"hours": 24,
"minutes": 1440,
"seconds": 3600
}
}
```
### Responses
#### 201 - CWT credential configuration created
```json
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"type": "CourseCredential",
"claimMappings": {
"firstName": {
"mapFrom": "claims.given_name",
"required": true
},
"address": {
"mapFrom": "claims.address.formatted"
},
"picture": {
"mapFrom": "claims.picture",
"defaultValue": "http://example.edu/img/placeholder.png"
},
"badge": {
"defaultValue": "http://example.edu/img/badge.png"
},
"providerSubjectId": {
"mapFrom": "authenticationProvider.subjectId"
}
},
"expiresIn": {
"years": 1,
"months": 12,
"weeks": 52,
"days": 365,
"hours": 24,
"minutes": 1440,
"seconds": 3600
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a CWT credential configuration
## Endpoint
```
GET /v2/credentials/compact/configurations/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/configurations/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns a CWT credential configuration by providing its ID.
### **Analytic events**
* CREDENTIAL_COMPACT_CREDENTIAL_CONFIGURATION_RETRIEVE_START
* CREDENTIAL_COMPACT_CREDENTIAL_CONFIGURATION_RETRIEVE_SUCCESS
* CREDENTIAL_COMPACT_CREDENTIAL_CONFIGURATION_RETRIEVE_FAIL
### Responses
#### 200 - CWT credential configuration retrieved
```json
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"type": "CourseCredential",
"claimMappings": {
"firstName": {
"mapFrom": "claims.given_name",
"required": true
},
"address": {
"mapFrom": "claims.address.formatted"
},
"picture": {
"mapFrom": "claims.picture",
"defaultValue": "http://example.edu/img/placeholder.png"
},
"badge": {
"defaultValue": "http://example.edu/img/badge.png"
},
"providerSubjectId": {
"mapFrom": "authenticationProvider.subjectId"
}
},
"expiresIn": {
"years": 1,
"months": 12,
"weeks": 52,
"days": 365,
"hours": 24,
"minutes": 1440,
"seconds": 3600
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a CWT credential configuration
## Endpoint
```
DELETE /v2/credentials/compact/configurations/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/configurations/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Deletes an existing CWT credential configuration by providing its ID.
### **Analytic events**
* CREDENTIAL_COMPACT_CREDENTIAL_CONFIGURATION_DELETE_START
* CREDENTIAL_COMPACT_CREDENTIAL_CONFIGURATION_DELETE_SUCCESS
* CREDENTIAL_COMPACT_CREDENTIAL_CONFIGURATION_DELETE_FAIL
### Responses
#### 204 - CWT credential configuration deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a CWT credential configuration
## Endpoint
```
PUT /v2/credentials/compact/configurations/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/configurations/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Updates an existing CWT credential configuration by providing its ID.
### **Analytic events**
* CREDENTIAL_COMPACT_CREDENTIAL_CONFIGURATION_UPDATE_START
* CREDENTIAL_COMPACT_CREDENTIAL_CONFIGURATION_UPDATE_SUCCESS
* CREDENTIAL_COMPACT_CREDENTIAL_CONFIGURATION_UPDATE_FAIL
### Request Body
Update a CWT credential configuration
```json
{
"type": "CourseCredential",
"claimMappings": {
"firstName": {
"mapFrom": "claims.given_name",
"required": true
},
"address": {
"mapFrom": "claims.address.formatted"
},
"picture": {
"mapFrom": "claims.picture",
"defaultValue": "http://example.edu/img/placeholder.png"
},
"badge": {
"defaultValue": "http://example.edu/img/badge.png"
},
"providerSubjectId": {
"mapFrom": "authenticationProvider.subjectId"
}
},
"expiresIn": {
"years": 1,
"months": 12,
"weeks": 52,
"days": 365,
"hours": 24,
"minutes": 1440,
"seconds": 3600
}
}
```
### Responses
#### 200 - CWT credential configuration updated
```json
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"type": "CourseCredential",
"claimMappings": {
"firstName": {
"mapFrom": "claims.given_name",
"required": true
},
"address": {
"mapFrom": "claims.address.formatted"
},
"picture": {
"mapFrom": "claims.picture",
"defaultValue": "http://example.edu/img/placeholder.png"
},
"badge": {
"defaultValue": "http://example.edu/img/badge.png"
},
"providerSubjectId": {
"mapFrom": "authenticationProvider.subjectId"
}
},
"expiresIn": {
"years": 1,
"months": 12,
"weeks": 52,
"days": 365,
"hours": 24,
"minutes": 1440,
"seconds": 3600
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Sign a Semantic CWT credential
## Endpoint
```
POST /v2/credentials/compact-semantic/sign
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/sign`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns a signed Semantic CWT credential generated from a provided valid payload.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_SIGN_START
* CREDENTIAL_COMPACT_SEMANTIC_SIGN_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_SIGN_FAIL
### Request Body
Semantic CWT credential payload to sign
```json
{
"payload": {
"iss": "did:web:organization.com",
"nbf": 1645743759,
"exp": 1645743759,
"iat": 1645743759,
"aud": "...",
"sub": "...",
"vc": {
"@context": "https://www.w3.org/2018/credentials/examples/v1",
"type": "AlumniCredential"
}
}
}
```
### Responses
#### 200 - Semantic CWT credential signed
```json
{
"encoded": "CSS:/1/BASE_32_ENCODED_PAYLOAD",
"decoded": {
"iss": "did:web:example.com",
"jti": "...",
"nbf": 1645743759,
"exp": 1645743759,
"iat": 1645743759,
"aud": "...",
"sub": "...",
"vc": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"type": [
"VerifiableCredential",
"AlumniCredential"
]
},
"status": {
"url": "...",
"index": 123
}
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Format a Semantic CWT credential as a QR code
## Endpoint
```
POST /v2/credentials/compact-semantic/qrcode
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/qrcode`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns a QR code representation of a Semantic CWT credential from a provided encoded string representation of that credential.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_QRCODE_CREATE_START
* CREDENTIAL_COMPACT_SEMANTIC_QRCODE_CREATE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_QRCODE_CREATE_FAIL
### Request Body
```json
{
"payload": "CSS:/1/2KCE3IQEJB5DCMSMGRKXI3IBE2QFSANKVACBUYQYB2HQKGTCDAHI6BQ2MIMA5DYBPAUWI2L...",
"width": 250
}
```
### Responses
#### 200 - QR code generated
#### 400 - Bad Request
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Format a CWT credential as a PDF
## Endpoint
```
POST /v2/credentials/compact-semantic/pdf
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/pdf`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns a PDF representation of a provided CWT credential based on an existing PDF template.
The request will fail if the provided credential isn't valid or has expired.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_PDF_CREATE_START
* CREDENTIAL_COMPACT_SEMANTIC_PDF_CREATE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_PDF_CREATE_FAIL
### Request Body
The credential payload
```json
{
"templateId": "4eea7654-d4c5-4eba-bd7a-5ca334d54725",
"payload": "{payload}"
}
```
### Responses
#### 200 - PDF created
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Format a Semantic CWT credential as an Apple Pass
## Endpoint
```
POST /v2/credentials/compact-semantic/digital-pass/apple
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/digital-pass/apple`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns an Apple Pass representation of a provided Semantic CWT credential based on an existing Apple Pass template.
The request will fail if the provided credential isn't valid or has expired.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_APPLE_PASS_CREATE_START
* CREDENTIAL_COMPACT_SEMANTIC_APPLE_PASS_CREATE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_APPLE_PASS_CREATE_FAIL
### Request Body
```json
{
"templateId": "3812166c-ac9f-4e4e-96dd-c1336b5be378",
"payload": "{payload}"
}
```
### Responses
#### 200 - Apple Pass created
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Format a Semantic CWT credential as a Google Pass
## Endpoint
```
POST /v2/credentials/compact-semantic/digital-pass/google
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/digital-pass/google`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns a Google Pass representation of a provided CWT credential based on an existing Google Pass template.
The request will fail if the provided credential isn't valid or has expired.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_GOOGLE_PASS_CREATE_START
* CREDENTIAL_COMPACT_SEMANTIC_GOOGLE_PASS_CREATE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_GOOGLE_PASS_CREATE_FAIL
### Request Body
```json
{
"templateId": "3812166c-ac9f-4e4e-96dd-c1336b5be378",
"payload": "{payload}"
}
```
### Responses
#### 200 - Google Pass created
```json
{
"redirectTo": "https://pay.google.com/gp/v/save/{jwt}"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Verify a Semantic CWT credential
## Endpoint
```
POST /v2/credentials/compact-semantic/verify
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/verify`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Verify a Semantic CWT credential by providing the encoded payload and specifying verification options.
You can provide a valid Semantic CWT credential as either an encoded string or a QR code in a PDF document or an image file.
Standard checks performed on all verification requests:
- Conformance of the string and encoded data. All string representations of CWT credentials must be prefixed with `CSC/1`.
- Decoded payload CWT structure and attributes can be validated.
- Remote context schema can be resolved and validate claims can be dereferenced.
- Issuer DID can be used to resolve its `did.json` document.
- Public key from issuer's `did.json` document validates the proof signature, confirming the credential has not been tampered with.
Optional parameter checks:
- Credential was issued by a trusted issuer.
- Current time is after the beginning of the credential validity period.
- Current time is not after the end of the credential validity period.
- Credential has not been revoked.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_VERIFY_START
* CREDENTIAL_COMPACT_SEMANTIC_VERIFY_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_VERIFY_FAIL
### Request Body
```json
{
"payload": "CSS:/1/2KCE3IQEJB5DCMSLN5KWKZABE2QFRMFEAF4CIZDJMQ5HOZLCHIYDGOJUFUYTENJNGIZTOLJVGIWTCMJQFZXGO4TPNMXGS33COZR2G2CAMNXW45DFPB2IC6BGNB2HI4DTHIXS653XO4XHOMZON5ZGOLZSGAYTQL3DOJSWIZLOORUWC3DTF53DCZDUPFYGLALUKZSXE2LGNFQWE3DFINZGKZDFNZ2GSYLMOFRXEZLEMVXHI2LBNRJXKYTKMVRXJILENZQW2ZLEJJXWQ3QH3BAFAW2MIRFQDICFCSNL5EIX4IISCEIFDJRFHCRRLBALWFYDLUVEKXHERNWHUDGJI3DDNNXSFWIRHUASBHGB2I7UHGPZMJEB3SMOFMBL3PABL5HUFSQLLGNE7YRKSAM3OAQN7F4LG365HL67BU",
"trustedIssuers": [
"did:web:example.com"
]
}
```
### Responses
#### 200 - Verification completed
```json
{
"decoded": {
"iss": "did:web:example.com",
"jti": "...",
"nbf": 1645743759,
"exp": 1645743759,
"iat": 1645743759,
"aud": "...",
"sub": "...",
"vc": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"type": [
"VerifiableCredential",
"AlumniCredential"
]
},
"status": {
"url": "...",
"index": 123
}
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 413 - Payload Too Large
#### 415 - Unsupported Media Type
# Delete Semantic CWT credential metadata
## Endpoint
```
DELETE /v2/credentials/compact-semantic/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Deletes all credential metadata from the tenant for a specific credential by providing its ID. If the credential was set to be revocable, it will be permanently revoked upon metadata deletion.
Note that only metadata of revocable credentials or credentials issued via the OID4VCI flow is saved.
Deleted metadata cannot be recovered.
### Responses
#### 204 - Credential metadata deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all Semantic CWT credential revocation lists
## Endpoint
```
GET /v2/credentials/compact-semantic/revocation-lists
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/revocation-lists`
### Authorization
None required.
## Description
Returns a list of all Semantic CWT credential revocation lists on the tenant.
This endpoint is intended for public consumption, and as such does not require authentication.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_REVOCATION_LISTS_RETRIEVE_START
* CREDENTIAL_COMPACT_SEMANTIC_REVOCATION_LISTS_RETRIEVE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_REVOCATION_LISTS_RETRIEVE_FAIL
### Responses
#### 200 - Revocation lists retrieved
# Retrieve Semantic CWT credential revocation list
## Endpoint
```
GET /v2/credentials/compact-semantic/revocation-lists/{listId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/revocation-lists/{listId}`
### Authorization
None required.
## Description
Returns a Semantic CWT credential revocation list by providing its ID.
This endpoint is intended for public consumption, and as such does not require authentication.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_REVOCATION_RETRIEVE_START
* CREDENTIAL_COMPACT_SEMANTIC_REVOCATION_RETRIEVE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_REVOCATION_RETRIEVE_FAIL
### Responses
#### 200 - Revocation list retrieved
# Retrieve Semantic CWT credential revocation status
## Endpoint
```
GET /v2/credentials/compact-semantic/{id}/revocation-status
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/{id}/revocation-status`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieve the revocation status of a Semantic CWT credential by providing its ID.
### Responses
#### 200 - Revocation status retrieved
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update Semantic CWT credential revocation status
## Endpoint
```
POST /v2/credentials/compact-semantic/{id}/revocation-status
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/{id}/revocation-status`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Updates the credential status as revoked (invalid) or unrevoked (valid).
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_REVOCATION_SET_STATUS_START
* CREDENTIAL_COMPACT_SEMANTIC_REVOCATION_SET_STATUS_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_REVOCATION_SET_STATUS_FAIL
### Request Body
Update revocation status
### Responses
#### 200 - Revocation status updated
#### 404 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all Semantic CWT credentials configurations
## Endpoint
```
GET /v2/credentials/compact-semantic/configurations
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/configurations`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns a list of all Compact Semantic Credential configurations from your tenant.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_CREDENTIAL_CONFIGURATION_RETRIEVE_LIST_START
* CREDENTIAL_COMPACT_SEMANTIC_CREDENTIAL_CONFIGURATION_RETRIEVE_LIST_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_CREDENTIAL_CONFIGURATION_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
- `type`: string
The optional credential type to filter on
### Responses
#### 200 - Semantic CWT credentials configurations retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM",
"data": [
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"type": "CourseCredential",
"claimMappings": {
"firstName": {
"mapFrom": "claims.given_name",
"required": true
},
"address": {
"mapFrom": "claims.address.formatted"
},
"picture": {
"mapFrom": "claims.picture",
"defaultValue": "http://example.edu/img/placeholder.png"
},
"badge": {
"defaultValue": "http://example.edu/img/badge.png"
},
"providerSubjectId": {
"mapFrom": "authenticationProvider.subjectId"
}
},
"expiresIn": {
"years": 1,
"months": 12,
"weeks": 52,
"days": 365,
"hours": 24,
"minutes": 1440,
"seconds": 3600
}
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create a Semantic CWT credentials configuration
## Endpoint
```
POST /v2/credentials/compact-semantic/configurations
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/configurations`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Creates a new Semantic CWT credentials configuration, a specific set of rules and parameters that are used to create and validate a particular type of verifiable credential. These rules and parameters define how the credential is structured and what data it contains when issued.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_CREDENTIAL_CONFIGURATION_CREATE_START
* CREDENTIAL_COMPACT_SEMANTIC_CREDENTIAL_CONFIGURATION_CREATE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_CREDENTIAL_CONFIGURATION_CREATE_FAIL
### Request Body
The Credential Configuration payload
```json
{
"type": "CourseCredential",
"claimMappings": {
"firstName": {
"mapFrom": "claims.given_name",
"required": true
},
"address": {
"mapFrom": "claims.address.formatted"
},
"picture": {
"mapFrom": "claims.picture",
"defaultValue": "http://example.edu/img/placeholder.png"
},
"badge": {
"defaultValue": "http://example.edu/img/badge.png"
},
"providerSubjectId": {
"mapFrom": "authenticationProvider.subjectId"
}
},
"expiresIn": {
"years": 1,
"months": 12,
"weeks": 52,
"days": 365,
"hours": 24,
"minutes": 1440,
"seconds": 3600
}
}
```
### Responses
#### 201 - Credential configuration created
```json
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"type": "CourseCredential",
"claimMappings": {
"firstName": {
"mapFrom": "claims.given_name",
"required": true
},
"address": {
"mapFrom": "claims.address.formatted"
},
"picture": {
"mapFrom": "claims.picture",
"defaultValue": "http://example.edu/img/placeholder.png"
},
"badge": {
"defaultValue": "http://example.edu/img/badge.png"
},
"providerSubjectId": {
"mapFrom": "authenticationProvider.subjectId"
}
},
"expiresIn": {
"years": 1,
"months": 12,
"weeks": 52,
"days": 365,
"hours": 24,
"minutes": 1440,
"seconds": 3600
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a Semantic CWT credentials configuration
## Endpoint
```
GET /v2/credentials/compact-semantic/configurations/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/configurations/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns a Semantic CWT credentials configuration by providing its ID.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_CREDENTIAL_CONFIGURATION_RETRIEVE_START
* CREDENTIAL_COMPACT_SEMANTIC_CREDENTIAL_CONFIGURATION_RETRIEVE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_CREDENTIAL_CONFIGURATION_RETRIEVE_FAIL
### Responses
#### 200 - Semantic CWT credentials configuration retrieved
```json
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"type": "CourseCredential",
"claimMappings": {
"firstName": {
"mapFrom": "claims.given_name",
"required": true
},
"address": {
"mapFrom": "claims.address.formatted"
},
"picture": {
"mapFrom": "claims.picture",
"defaultValue": "http://example.edu/img/placeholder.png"
},
"badge": {
"defaultValue": "http://example.edu/img/badge.png"
},
"providerSubjectId": {
"mapFrom": "authenticationProvider.subjectId"
}
},
"expiresIn": {
"years": 1,
"months": 12,
"weeks": 52,
"days": 365,
"hours": 24,
"minutes": 1440,
"seconds": 3600
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a Semantic CWT credentials configuration
## Endpoint
```
DELETE /v2/credentials/compact-semantic/configurations/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/configurations/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Deletes an existing Semantic CWT credentials configuration by providing its ID.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_CREDENTIAL_CONFIGURATION_DELETE_START
* CREDENTIAL_COMPACT_SEMANTIC_CREDENTIAL_CONFIGURATION_DELETE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_CREDENTIAL_CONFIGURATION_DELETE_FAIL
### Responses
#### 204 - Semantic CWT credentials configuration deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a Semantic CWT credentials configuration
## Endpoint
```
PUT /v2/credentials/compact-semantic/configurations/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/configurations/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Updates an existing Semantic CWT credentials configuration by providing its ID.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_CREDENTIAL_CONFIGURATION_UPDATE_START
* CREDENTIAL_COMPACT_SEMANTIC_CREDENTIAL_CONFIGURATION_UPDATE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_CREDENTIAL_CONFIGURATION_UPDATE_FAIL
### Request Body
Update a Credential Configuration
```json
{
"type": "CourseCredential",
"claimMappings": {
"firstName": {
"mapFrom": "claims.given_name",
"required": true
},
"address": {
"mapFrom": "claims.address.formatted"
},
"picture": {
"mapFrom": "claims.picture",
"defaultValue": "http://example.edu/img/placeholder.png"
},
"badge": {
"defaultValue": "http://example.edu/img/badge.png"
},
"providerSubjectId": {
"mapFrom": "authenticationProvider.subjectId"
}
},
"expiresIn": {
"years": 1,
"months": 12,
"weeks": 52,
"days": 365,
"hours": 24,
"minutes": 1440,
"seconds": 3600
}
}
```
### Responses
#### 200 - Semantic CWT credentials configuration updated
```json
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"type": "CourseCredential",
"claimMappings": {
"firstName": {
"mapFrom": "claims.given_name",
"required": true
},
"address": {
"mapFrom": "claims.address.formatted"
},
"picture": {
"mapFrom": "claims.picture",
"defaultValue": "http://example.edu/img/placeholder.png"
},
"badge": {
"defaultValue": "http://example.edu/img/badge.png"
},
"providerSubjectId": {
"mapFrom": "authenticationProvider.subjectId"
}
},
"expiresIn": {
"years": 1,
"months": 12,
"weeks": 52,
"days": 365,
"hours": 24,
"minutes": 1440,
"seconds": 3600
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all PDF templates
## Endpoint
```
GET /v2/credentials/compact/pdf/templates
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/pdf/templates`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves a list of all CWT credentials PDF templates available on the tenant.
### **Analytic events**
* CREDENTIAL_COMPACT_PDF_TEMPLATE_RETRIEVE_LIST_START
* CREDENTIAL_COMPACT_PDF_TEMPLATE_RETRIEVE_LIST_SUCCESS
* CREDENTIAL_COMPACT_PDF_TEMPLATE_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - PDF templates retrieved
```json
{
"nextCursor": "0ecdcb57-ef2b-4aa1-be34-695c2d9d9486",
"data": [
{
"id": "4eea7654-d4c5-4eba-bd7a-5ca334d54725",
"name": "Certificate of participation",
"fileName": "certificate_of_participation",
"fonts": [
{
"name": "PublicSans-Regular",
"fileName": "fonts/PublicSans-Regular.ttf"
}
],
"metadata": {
"title": ""
},
"fields": [
{
"key": "familyName",
"value": "{{payload.sub_claims.familyName}}",
"isRequired": true,
"alternativeText": "{{payload.sub_claims.familyName}}",
"fontName": "PublicSans-Regular"
}
]
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create a PDF template
## Endpoint
```
POST /v2/credentials/compact/pdf/templates
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/pdf/templates`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Creates a CWT credential PDF template based on a provided `.zip` file. Refer to our [PDF template design guide](https://learn.mattr.global/docs/issuance/cwt-credential-templates/pdf-templates) for more information on how to design a template and structure the `.zip` file.
### **Analytic events**
* CREDENTIAL_COMPACT_PDF_TEMPLATE_CREATE_START
* CREDENTIAL_COMPACT_PDF_TEMPLATE_CREATE_SUCCESS
* CREDENTIAL_COMPACT_PDF_TEMPLATE_CREATE_FAIL
### Request Body
### Responses
#### 200 - PDF template created
```json
{
"id": "4eea7654-d4c5-4eba-bd7a-5ca334d54725",
"name": "Certificate of participation",
"fileName": "certificate_of_participation",
"fonts": [
{
"name": "PublicSans-Regular",
"fileName": "fonts/PublicSans-Regular.ttf"
}
],
"metadata": {
"title": ""
},
"fields": [
{
"key": "familyName",
"value": "{{payload.sub_claims.familyName}}",
"isRequired": true,
"alternativeText": "{{payload.sub_claims.familyName}}",
"fontName": "PublicSans-Regular"
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a PDF template
## Endpoint
```
GET /v2/credentials/compact/pdf/templates/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/pdf/templates/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves an existing CWT credentials PDF template using its ID.
### **Analytic events**
* CREDENTIAL_COMPACT_PDF_TEMPLATE_RETRIEVE_START
* CREDENTIAL_COMPACT_PDF_TEMPLATE_RETRIEVE_SUCCESS
* CREDENTIAL_COMPACT_PDF_TEMPLATE_RETRIEVE_FAIL
### Path Parameters
- `id`: string (required)
PDF template ID
### Responses
#### 200 - PDF template retrieved
```json
{
"id": "4eea7654-d4c5-4eba-bd7a-5ca334d54725",
"name": "Certificate of participation",
"fileName": "certificate_of_participation",
"fonts": [
{
"name": "PublicSans-Regular",
"fileName": "fonts/PublicSans-Regular.ttf"
}
],
"metadata": {
"title": ""
},
"fields": [
{
"key": "familyName",
"value": "{{payload.sub_claims.familyName}}",
"isRequired": true,
"alternativeText": "{{payload.sub_claims.familyName}}",
"fontName": "PublicSans-Regular"
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a PDF template
## Endpoint
```
DELETE /v2/credentials/compact/pdf/templates/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/pdf/templates/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Delete an existing PDF template by providing its ID
### **Analytic events**
* CREDENTIAL_COMPACT_PDF_TEMPLATE_DELETE_START
* CREDENTIAL_COMPACT_PDF_TEMPLATE_DELETE_SUCCESS
* CREDENTIAL_COMPACT_PDF_TEMPLATE_DELETE_FAIL
### Path Parameters
- `id`: string (required)
PDF Template ID
### Responses
#### 204 - PDF template deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a PDF template
## Endpoint
```
PUT /v2/credentials/compact/pdf/templates/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/pdf/templates/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Update an existing CWT credential PDF template based on a provided `.zip` file. Refer to our [PDF template design guide](https://learn.mattr.global/docs/issuance/cwt-credential-templates/pdf-templates) for more information on how to design a template and structure the `.zip` file.
### **Analytic events**
* CREDENTIAL_COMPACT_PDF_TEMPLATE_UPDATE_START
* CREDENTIAL_COMPACT_PDF_TEMPLATE_UPDATE_SUCCESS
* CREDENTIAL_COMPACT_PDF_TEMPLATE_UPDATE_FAIL
### Path Parameters
- `id`: string (required)
PDF template ID
### Request Body
### Responses
#### 200 - PDF template updated
```json
{
"id": "4eea7654-d4c5-4eba-bd7a-5ca334d54725",
"name": "Certificate of participation",
"fileName": "certificate_of_participation",
"fonts": [
{
"name": "PublicSans-Regular",
"fileName": "fonts/PublicSans-Regular.ttf"
}
],
"metadata": {
"title": ""
},
"fields": [
{
"key": "familyName",
"value": "{{payload.sub_claims.familyName}}",
"isRequired": true,
"alternativeText": "{{payload.sub_claims.familyName}}",
"fontName": "PublicSans-Regular"
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all PDF templates
## Endpoint
```
GET /v2/credentials/compact-semantic/pdf/templates
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/pdf/templates`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves a list of all Semantic CWT credential PDF templates available on the tenant.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_PDF_TEMPLATE_RETRIEVE_LIST_START
* CREDENTIAL_COMPACT_SEMANTIC_PDF_TEMPLATE_RETRIEVE_LIST_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_PDF_TEMPLATE_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - PDF templates retrieved
```json
{
"nextCursor": "0ecdcb57-ef2b-4aa1-be34-695c2d9d9486",
"data": [
{
"id": "4eea7654-d4c5-4eba-bd7a-5ca334d54725",
"name": "Certificate of participation",
"fileName": "certificate_of_participation",
"fonts": [
{
"name": "PublicSans-Regular",
"fileName": "fonts/PublicSans-Regular.ttf"
}
],
"metadata": {
"title": ""
},
"fields": [
{
"key": "familyName",
"value": "{{payload.sub_claims.familyName}}",
"isRequired": true,
"alternativeText": "{{payload.sub_claims.familyName}}",
"fontName": "PublicSans-Regular"
}
]
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create a PDF template
## Endpoint
```
POST /v2/credentials/compact-semantic/pdf/templates
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/pdf/templates`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Creates a Semantic CWT credential PDF template based on a provided `.zip` file. Refer to our [PDF template design guide](https://learn.mattr.global/docs/issuance/cwt-credential-templates/pdf-templates) for more information on how to design a template and structure the `.zip` file.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_PDF_TEMPLATE_CREATE_START
* CREDENTIAL_COMPACT_SEMANTIC_PDF_TEMPLATE_CREATE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_PDF_TEMPLATE_CREATE_FAIL
### Request Body
### Responses
#### 200 - PDF template created
```json
{
"id": "4eea7654-d4c5-4eba-bd7a-5ca334d54725",
"name": "Certificate of participation",
"fileName": "certificate_of_participation",
"fonts": [
{
"name": "PublicSans-Regular",
"fileName": "fonts/PublicSans-Regular.ttf"
}
],
"metadata": {
"title": ""
},
"fields": [
{
"key": "familyName",
"value": "{{payload.sub_claims.familyName}}",
"isRequired": true,
"alternativeText": "{{payload.sub_claims.familyName}}",
"fontName": "PublicSans-Regular"
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a PDF template
## Endpoint
```
GET /v2/credentials/compact-semantic/pdf/templates/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/pdf/templates/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves an existing Semantic CWT credential PDF template using its ID.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_PDF_TEMPLATE_RETRIEVE_START
* CREDENTIAL_COMPACT_SEMANTIC_PDF_TEMPLATE_RETRIEVE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_PDF_TEMPLATE_RETRIEVE_FAIL
### Path Parameters
- `id`: string (required)
PDF Template ID
### Responses
#### 200 - PDF template retrieved
```json
{
"id": "4eea7654-d4c5-4eba-bd7a-5ca334d54725",
"name": "Certificate of participation",
"fileName": "certificate_of_participation",
"fonts": [
{
"name": "PublicSans-Regular",
"fileName": "fonts/PublicSans-Regular.ttf"
}
],
"metadata": {
"title": ""
},
"fields": [
{
"key": "familyName",
"value": "{{payload.sub_claims.familyName}}",
"isRequired": true,
"alternativeText": "{{payload.sub_claims.familyName}}",
"fontName": "PublicSans-Regular"
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a PDF template
## Endpoint
```
DELETE /v2/credentials/compact-semantic/pdf/templates/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/pdf/templates/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Delete an existing PDF template by providing its ID
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_PDF_TEMPLATE_DELETE_START
* CREDENTIAL_COMPACT_SEMANTIC_PDF_TEMPLATE_DELETE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_PDF_TEMPLATE_DELETE_FAIL
### Path Parameters
- `id`: string (required)
PDF Template ID
### Responses
#### 204 - PDF template deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a PDF template
## Endpoint
```
PUT /v2/credentials/compact-semantic/pdf/templates/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/pdf/templates/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Update an existing CWT credential PDF template based on a provided `.zip` file. Refer to our [PDF template design guide](https://learn.mattr.global/docs/issuance/cwt-credential-templates/pdf-templates) for more information on how to design a template and structure the `.zip` file.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_PDF_TEMPLATE_UPDATE_START
* CREDENTIAL_COMPACT_SEMANTIC_PDF_TEMPLATE_UPDATE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_PDF_TEMPLATE_UPDATE_FAIL
### Path Parameters
- `id`: string (required)
PDF Template ID
### Request Body
### Responses
#### 200 - PDF template updated
```json
{
"id": "4eea7654-d4c5-4eba-bd7a-5ca334d54725",
"name": "Certificate of participation",
"fileName": "certificate_of_participation",
"fonts": [
{
"name": "PublicSans-Regular",
"fileName": "fonts/PublicSans-Regular.ttf"
}
],
"metadata": {
"title": ""
},
"fields": [
{
"key": "familyName",
"value": "{{payload.sub_claims.familyName}}",
"isRequired": true,
"alternativeText": "{{payload.sub_claims.familyName}}",
"fontName": "PublicSans-Regular"
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all Apple Pass templates
## Endpoint
```
GET /v2/credentials/compact/digital-pass/apple/templates
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/digital-pass/apple/templates`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves a list of all Apple Pass template available on the tenant.
### **Analytic events**
* CREDENTIAL_COMPACT_APPLE_PASS_TEMPLATE_RETRIEVE_LIST_START
* CREDENTIAL_COMPACT_APPLE_PASS_TEMPLATE_RETRIEVE_LIST_SUCCESS
* CREDENTIAL_COMPACT_APPLE_PASS_TEMPLATE_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Apple Pass templates retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1h",
"data": [
{
"id": "3812166c-ac9f-4e4e-96dd-c1336b5be378",
"name": "Certificate of participation",
"metadata": {
"fileName": "certificate_of_participation.pkpass",
"teamIdentifier": "GH5P43ABC",
"passTypeIdentifier": "pass.myproject.participation.pk"
}
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create an Apple Pass template
## Endpoint
```
POST /v2/credentials/compact/digital-pass/apple/templates
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/digital-pass/apple/templates`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Creates an Apple Pass template based on the provided `.zip` file. Refer to our [Design an Apple Pass template](https://learn.mattr.global/docs/issuance/cwt-credential-templates/apple-templates) guide for more information on how to design the template and how to structure the `.zip` file.
The Apple Pass template uses the official Apple Pass bundle structure.
### **Analytic events**
* CREDENTIAL_COMPACT_APPLE_PASS_TEMPLATE_CREATE_START
* CREDENTIAL_COMPACT_APPLE_PASS_TEMPLATE_CREATE_SUCCESS
* CREDENTIAL_COMPACT_APPLE_PASS_TEMPLATE_CREATE_FAIL
### Request Body
### Responses
#### 201 - Apple Pass template created
```json
{
"id": "3812166c-ac9f-4e4e-96dd-c1336b5be378",
"name": "Certificate of participation",
"metadata": {
"fileName": "certificate_of_participation.pkpass",
"teamIdentifier": "GH5P43ABC",
"passTypeIdentifier": "pass.myproject.participation.pk"
}
}
```
#### 400 - Bad Request
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve an Apple Pass template
## Endpoint
```
GET /v2/credentials/compact/digital-pass/apple/templates/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/digital-pass/apple/templates/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves an existing Apple Pass template by providing its ID.
### **Analytic events**
* CREDENTIAL_COMPACT_APPLE_PASS_TEMPLATE_RETRIEVE_START
* CREDENTIAL_COMPACT_APPLE_PASS_TEMPLATE_RETRIEVE_SUCCESS
* CREDENTIAL_COMPACT_APPLE_PASS_TEMPLATE_RETRIEVE_FAIL
### Path Parameters
- `id`: string (required)
Apple Pass template ID
### Responses
#### 200 - Apple Pass template retrieved
```json
{
"id": "3812166c-ac9f-4e4e-96dd-c1336b5be378",
"name": "Certificate of participation",
"metadata": {
"fileName": "certificate_of_participation.pkpass",
"teamIdentifier": "GH5P43ABC",
"passTypeIdentifier": "pass.myproject.participation.pk"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete an Apple Pass template
## Endpoint
```
DELETE /v2/credentials/compact/digital-pass/apple/templates/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/digital-pass/apple/templates/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Deletes an existing Apple Pass template by providing its ID.
### **Analytic events**
* CREDENTIAL_COMPACT_APPLE_PASS_TEMPLATE_DELETE_START
* CREDENTIAL_COMPACT_APPLE_PASS_TEMPLATE_DELETE_SUCCESS
* CREDENTIAL_COMPACT_APPLE_PASS_TEMPLATE_DELETE_FAIL
### Path Parameters
- `id`: string (required)
Apple Pass template ID
### Responses
#### 204 - Apple Pass template deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update an Apple Pass template
## Endpoint
```
PUT /v2/credentials/compact/digital-pass/apple/templates/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/digital-pass/apple/templates/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Updates an existing Apple Pass template by providing its ID and a `.zip` file. Refer to our [Design an Apple Pass template](https://learn.mattr.global/docs/issuance/cwt-credential-templates/apple-templates) guide for more information on how to design the template and how to structure the `.zip` file.
### **Analytic events**
* CREDENTIAL_COMPACT_APPLE_PASS_TEMPLATE_UPDATE_START
* CREDENTIAL_COMPACT_APPLE_PASS_TEMPLATE_UPDATE_SUCCESS
* CREDENTIAL_COMPACT_APPLE_PASS_TEMPLATE_UPDATE_FAIL
### Path Parameters
- `id`: string (required)
Apple Pass template ID
### Request Body
### Responses
#### 200 - Apple Pass template updated
```json
{
"id": "3812166c-ac9f-4e4e-96dd-c1336b5be378",
"name": "Certificate of participation",
"metadata": {
"fileName": "certificate_of_participation.pkpass",
"teamIdentifier": "GH5P43ABC",
"passTypeIdentifier": "pass.myproject.participation.pk"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all Google Pass templates
## Endpoint
```
GET /v2/credentials/compact/digital-pass/google/templates
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/digital-pass/google/templates`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves a list of all Google Pass templates available on your tenant.
### **Analytic events**
* CREDENTIAL_COMPACT_GOOGLE_PASS_TEMPLATE_RETRIEVE_LIST_START
* CREDENTIAL_COMPACT_GOOGLE_PASS_TEMPLATE_RETRIEVE_LIST_SUCCESS
* CREDENTIAL_COMPACT_GOOGLE_PASS_TEMPLATE_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Google Pass templates retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1h",
"data": [
{
"id": "3812166c-ac9f-4e4e-96dd-c1336b5be378",
"name": "Certificate of participation",
"metadata": {
"issuerId": "3388000000012346000",
"serviceAccountClientEmail": "app-user@myproject.iam.gserviceaccount.com",
"payPassId": "3388000000012345678.a0bbe92f-c85e-4081-94c3-f842bcd5e463"
}
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create a Google Pass template
## Endpoint
```
POST /v2/credentials/compact/digital-pass/google/templates
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/digital-pass/google/templates`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Creates a Google Pass template based on the provided `.zip` file. Refer to our [Design a Google Pass template](https://learn.mattr.global/docs/issuance/cwt-credential-templates/google-templates) guide for more information on how to design the template and how to structure the `.zip` file.
### **Analytic events**
* CREDENTIAL_COMPACT_GOOGLE_PASS_TEMPLATE_CREATE_START
* CREDENTIAL_COMPACT_GOOGLE_PASS_TEMPLATE_CREATE_SUCCESS
* CREDENTIAL_COMPACT_GOOGLE_PASS_TEMPLATE_CREATE_FAIL
### Request Body
### Responses
#### 201 - Google Pass template created
```json
{
"id": "3812166c-ac9f-4e4e-96dd-c1336b5be378",
"name": "Certificate of participation",
"metadata": {
"issuerId": "3388000000012346000",
"serviceAccountClientEmail": "app-user@myproject.iam.gserviceaccount.com",
"payPassId": "3388000000012345678.a0bbe92f-c85e-4081-94c3-f842bcd5e463"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a Google Pass template
## Endpoint
```
GET /v2/credentials/compact/digital-pass/google/templates/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/digital-pass/google/templates/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves an existing Google Pass template by providing its ID.
### **Analytic events**
* CREDENTIAL_COMPACT_GOOGLE_PASS_TEMPLATE_RETRIEVE_START
* CREDENTIAL_COMPACT_GOOGLE_PASS_TEMPLATE_RETRIEVE_SUCCESS
* CREDENTIAL_COMPACT_GOOGLE_PASS_TEMPLATE_RETRIEVE_FAIL
### Path Parameters
- `id`: string (required)
Google Pass template ID
### Responses
#### 200 - Google Pass templated retrieved
```json
{
"id": "3812166c-ac9f-4e4e-96dd-c1336b5be378",
"name": "Certificate of participation",
"metadata": {
"issuerId": "3388000000012346000",
"serviceAccountClientEmail": "app-user@myproject.iam.gserviceaccount.com",
"payPassId": "3388000000012345678.a0bbe92f-c85e-4081-94c3-f842bcd5e463"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a Google Pass template
## Endpoint
```
DELETE /v2/credentials/compact/digital-pass/google/templates/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/digital-pass/google/templates/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Deletes an existing Google Pass template by providing its ID.
### **Analytic events**
* CREDENTIAL_COMPACT_GOOGLE_PASS_TEMPLATE_DELETE_START
* CREDENTIAL_COMPACT_GOOGLE_PASS_TEMPLATE_DELETE_SUCCESS
* CREDENTIAL_COMPACT_GOOGLE_PASS_TEMPLATE_DELETE_FAIL
### Path Parameters
- `id`: string (required)
Google Pass template ID
### Responses
#### 204 - Google Pass template deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a Google Pass template
## Endpoint
```
PUT /v2/credentials/compact/digital-pass/google/templates/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact/digital-pass/google/templates/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Updates a existing Google Pass template by providing its ID and a `.zip` file. Refer to our [Design a Google Pass template](https://learn.mattr.global/docs/issuance/cwt-credential-templates/google-templates) guide for more information on how to design the template and how to structure the `.zip` file.
### **Analytic events**
* CREDENTIAL_COMPACT_GOOGLE_PASS_TEMPLATE_UPDATE_START
* CREDENTIAL_COMPACT_GOOGLE_PASS_TEMPLATE_UPDATE_SUCCESS
* CREDENTIAL_COMPACT_GOOGLE_PASS_TEMPLATE_UPDATE_FAIL
### Path Parameters
- `id`: string (required)
Google Pass template ID
### Request Body
### Responses
#### 200 - Google Pass template updated
```json
{
"id": "3812166c-ac9f-4e4e-96dd-c1336b5be378",
"name": "Certificate of participation",
"metadata": {
"issuerId": "3388000000012346000",
"serviceAccountClientEmail": "app-user@myproject.iam.gserviceaccount.com",
"payPassId": "3388000000012345678.a0bbe92f-c85e-4081-94c3-f842bcd5e463"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all Apple Pass templates
## Endpoint
```
GET /v2/credentials/compact-semantic/digital-pass/apple/templates
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/digital-pass/apple/templates`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves a list of all Apple Pass template available on the tenant.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_APPLE_PASS_TEMPLATE_RETRIEVE_LIST_START
* CREDENTIAL_COMPACT_SEMANTIC_APPLE_PASS_TEMPLATE_RETRIEVE_LIST_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_APPLE_PASS_TEMPLATE_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Apple Pass templates retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1h",
"data": [
{
"id": "3812166c-ac9f-4e4e-96dd-c1336b5be378",
"name": "Certificate of participation",
"metadata": {
"fileName": "certificate_of_participation.pkpass",
"teamIdentifier": "GH5P43ABC",
"passTypeIdentifier": "pass.myproject.participation.pk"
}
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create an Apple Pass template
## Endpoint
```
POST /v2/credentials/compact-semantic/digital-pass/apple/templates
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/digital-pass/apple/templates`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Creates an Apple Pass template based on the provided `.zip` file. Refer to our [Design an Apple Pass template](https://learn.mattr.global/docs/issuance/cwt-credential-templates/apple-templates) guide for more information on how to design the template and how to structure the `.zip` file.
The Apple Pass template uses the official Apple Pass bundle structure.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_APPLE_PASS_TEMPLATE_CREATE_START
* CREDENTIAL_COMPACT_SEMANTIC_APPLE_PASS_TEMPLATE_CREATE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_APPLE_PASS_TEMPLATE_CREATE_FAIL
### Request Body
### Responses
#### 201 - Apple Pass template created
```json
{
"id": "3812166c-ac9f-4e4e-96dd-c1336b5be378",
"name": "Certificate of participation",
"metadata": {
"fileName": "certificate_of_participation.pkpass",
"teamIdentifier": "GH5P43ABC",
"passTypeIdentifier": "pass.myproject.participation.pk"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve an Apple Pass template
## Endpoint
```
GET /v2/credentials/compact-semantic/digital-pass/apple/templates/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/digital-pass/apple/templates/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves an existing Apple Pass template by providing its ID.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_APPLE_PASS_TEMPLATE_RETRIEVE_START
* CREDENTIAL_COMPACT_SEMANTIC_APPLE_PASS_TEMPLATE_RETRIEVE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_APPLE_PASS_TEMPLATE_RETRIEVE_FAIL
### Path Parameters
- `id`: string (required)
Apple Pass template ID
### Responses
#### 200 - Apple Pass template retrieved
```json
{
"id": "3812166c-ac9f-4e4e-96dd-c1336b5be378",
"name": "Certificate of participation",
"metadata": {
"fileName": "certificate_of_participation.pkpass",
"teamIdentifier": "GH5P43ABC",
"passTypeIdentifier": "pass.myproject.participation.pk"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete an Apple Pass template
## Endpoint
```
DELETE /v2/credentials/compact-semantic/digital-pass/apple/templates/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/digital-pass/apple/templates/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Deletes an existing Apple Pass template by providing its ID.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_APPLE_PASS_TEMPLATE_DELETE_START
* CREDENTIAL_COMPACT_SEMANTIC_APPLE_PASS_TEMPLATE_DELETE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_APPLE_PASS_TEMPLATE_DELETE_FAIL
### Path Parameters
- `id`: string (required)
Apple Pass template ID
### Responses
#### 204 - Apple Pass template deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update an Apple Pass template
## Endpoint
```
PUT /v2/credentials/compact-semantic/digital-pass/apple/templates/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/digital-pass/apple/templates/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Updates an existing Apple Pass template by providing its ID and a `.zip` file. Refer to our [Design an Apple Pass template](https://learn.mattr.global/docs/issuance/cwt-credential-templates/apple-templates) guide for more information on how to design the template and how to structure the `.zip` file.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_APPLE_PASS_TEMPLATE_UPDATE_START
* CREDENTIAL_COMPACT_SEMANTIC_APPLE_PASS_TEMPLATE_UPDATE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_APPLE_PASS_TEMPLATE_UPDATE_FAIL
### Path Parameters
- `id`: string (required)
Apple Pass template ID
### Request Body
### Responses
#### 200 - Apple Pass template updated
```json
{
"id": "3812166c-ac9f-4e4e-96dd-c1336b5be378",
"name": "Certificate of participation",
"metadata": {
"fileName": "certificate_of_participation.pkpass",
"teamIdentifier": "GH5P43ABC",
"passTypeIdentifier": "pass.myproject.participation.pk"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all Google Pass templates
## Endpoint
```
GET /v2/credentials/compact-semantic/digital-pass/google/templates
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/digital-pass/google/templates`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves a list of all Google Pass templates available on your tenant.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_GOOGLE_PASS_TEMPLATE_RETRIEVE_LIST_START
* CREDENTIAL_COMPACT_SEMANTIC_GOOGLE_PASS_TEMPLATE_RETRIEVE_LIST_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_GOOGLE_PASS_TEMPLATE_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Google Pass templates retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1h",
"data": [
{
"id": "3812166c-ac9f-4e4e-96dd-c1336b5be378",
"name": "Certificate of participation",
"metadata": {
"issuerId": "3388000000012346000",
"serviceAccountClientEmail": "app-user@myproject.iam.gserviceaccount.com",
"payPassId": "3388000000012345678.a0bbe92f-c85e-4081-94c3-f842bcd5e463"
}
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create a Google Pass template
## Endpoint
```
POST /v2/credentials/compact-semantic/digital-pass/google/templates
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/digital-pass/google/templates`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Creates a Google Pass template based on the provided `.zip` file. Refer to our [Design a Google Pass template](https://learn.mattr.global/docs/issuance/cwt-credential-templates/google-templates) guide for more information on how to design the template and how to structure the `.zip` file.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_GOOGLE_PASS_TEMPLATE_CREATE_START
* CREDENTIAL_COMPACT_SEMANTIC_GOOGLE_PASS_TEMPLATE_CREATE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_GOOGLE_PASS_TEMPLATE_CREATE_FAIL
### Request Body
### Responses
#### 201 - Google Pass template created
```json
{
"id": "3812166c-ac9f-4e4e-96dd-c1336b5be378",
"name": "Certificate of participation",
"metadata": {
"issuerId": "3388000000012346000",
"serviceAccountClientEmail": "app-user@myproject.iam.gserviceaccount.com",
"payPassId": "3388000000012345678.a0bbe92f-c85e-4081-94c3-f842bcd5e463"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a Google Pass template
## Endpoint
```
GET /v2/credentials/compact-semantic/digital-pass/google/templates/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/digital-pass/google/templates/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves an existing Google Pass template by providing its ID.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_GOOGLE_PASS_TEMPLATE_RETRIEVE_START
* CREDENTIAL_COMPACT_SEMANTIC_GOOGLE_PASS_TEMPLATE_RETRIEVE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_GOOGLE_PASS_TEMPLATE_RETRIEVE_FAIL
### Path Parameters
- `id`: string (required)
Google Pass template ID
### Responses
#### 200 - Google Pass templated retrieved
```json
{
"id": "3812166c-ac9f-4e4e-96dd-c1336b5be378",
"name": "Certificate of participation",
"metadata": {
"issuerId": "3388000000012346000",
"serviceAccountClientEmail": "app-user@myproject.iam.gserviceaccount.com",
"payPassId": "3388000000012345678.a0bbe92f-c85e-4081-94c3-f842bcd5e463"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a Google Pass template
## Endpoint
```
DELETE /v2/credentials/compact-semantic/digital-pass/google/templates/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/digital-pass/google/templates/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Delete a Google Pay Pass template by ID
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_GOOGLE_PASS_TEMPLATE_DELETE_START
* CREDENTIAL_COMPACT_SEMANTIC_GOOGLE_PASS_TEMPLATE_DELETE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_GOOGLE_PASS_TEMPLATE_DELETE_FAIL
### Path Parameters
- `id`: string (required)
Google Pass template ID
### Responses
#### 204 - Google Pass template deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a Google Pass template
## Endpoint
```
PUT /v2/credentials/compact-semantic/digital-pass/google/templates/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/compact-semantic/digital-pass/google/templates/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Updates a existing Google Pass template by providing its ID and a `.zip` file. Refer to our [Design a Google Pass template](https://learn.mattr.global/docs/issuance/cwt-credential-templates/google-templates) guide for more information on how to design the template and how to structure the `.zip` file.
### **Analytic events**
* CREDENTIAL_COMPACT_SEMANTIC_GOOGLE_PASS_TEMPLATE_UPDATE_START
* CREDENTIAL_COMPACT_SEMANTIC_GOOGLE_PASS_TEMPLATE_UPDATE_SUCCESS
* CREDENTIAL_COMPACT_SEMANTIC_GOOGLE_PASS_TEMPLATE_UPDATE_FAIL
### Path Parameters
- `id`: string (required)
Google Pass template ID
### Request Body
### Responses
#### 200 - Google Pass template updated
```json
{
"id": "3812166c-ac9f-4e4e-96dd-c1336b5be378",
"name": "Certificate of participation",
"metadata": {
"issuerId": "3388000000012346000",
"serviceAccountClientEmail": "app-user@myproject.iam.gserviceaccount.com",
"payPassId": "3388000000012345678.a0bbe92f-c85e-4081-94c3-f842bcd5e463"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all IACAs
## Endpoint
```
GET /v2/credentials/mobile/iacas
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/iacas`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves all existing IACAs from the tenant.
### **Analytic events**
* MOBILE_CREDENTIAL_IACA_RETRIEVE_LIST_START
* MOBILE_CREDENTIAL_IACA_RETRIEVE_LIST_SUCCESS
* MOBILE_CREDENTIAL_IACA_RETRIEVE_LIST_FAIL
### Responses
#### 200 - IACAs Retrieved
```json
{
"data": [
{
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICDjCCAbSgAwIBAgIKdeZsA5NPKimuAzAKBggqhkjOPQQDAjAiMSAwCQYDVQQG\r\nEwJOWjATBgNVBAMTDEV4YW1wbGUgSUFDQTAeFw0yMzA5MTEyMzM0MjJaFw0zMzA5\r\nMDgyMzM0MjJaMCIxIDAJBgNVBAYTAk5aMBMGA1UEAxMMRXhhbXBsZSBJQUNBMFkw\r\nEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBbK7JKKFMWuu8kHQK2qaML+MQ0Ykk3Qg\r\n/p3TC6lQKvYJozPSpLXbJQIzMPq9u/dG+j4vq1iX/G/jFIwfiEiKEqOB0TCBzjAS\r\nBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIABjAdBgNVHQ4EFgQU9zTh\r\nKsqFxAgRJDDGW1au+ewJK6owHgYDVR0SBBcwFYYTaHR0cHM6Ly9leGFtcGxlLmNv\r\nbTBpBgNVHR8EYjBgMF6gXKBahlhodHRwczovL2V4YW1wbGUuY29tL3YyL2NyZWRl\r\nbnRpYWxzL21vYmlsZS9pYWNhcy8yZTg5YzE1Ni0zMWQ1LTQ3ODMtYmQ1OS05MDU1\r\nYjVmOGU3ZDIvY3JsMAoGCCqGSM49BAMCA0gAMEUCIQDD+eU8iOsYYC0v41L94fhF\r\nZ0brPo4gx2aRxrhE3NLFpwIgIgHCPBXJ+JICJg3K7dEsr153So4SEZzAA1rRn4eF\r\nvkM=\r\n-----END CERTIFICATE-----\r\n",
"certificateData": {
"notAfter": "2034-09-26",
"notBefore": "2023-09-26",
"country": "US",
"commonName": "{tenant-subdomain}.vii.mattr.global IACA",
"stateOrProvinceName": "US-AL"
},
"certificateFingerprint": "3c06145a53e6c252091a71540f870d4d521dede9f176a681a74e38ddc47bb311",
"isManaged": true
}
]
}
```
# Create an IACA
## Endpoint
```
POST /v2/credentials/mobile/iacas
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/iacas`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Creates a new IACA that can be used to sign certificates for Document and Status List signers.
- IACAs are always created as inactive. You must manually [update](#operation/update-mobile-credential-iaca) the IACA to [`active: true`](#operation/update-mobile-credential-iaca!path=active&t=request) before it can be used to sign mDocs.
- A maximum of three IACAs can be created per tenant.
### **Analytic events**
* MOBILE_CREDENTIAL_IACA_CREATE_START
* MOBILE_CREDENTIAL_IACA_CREATE_SUCCESS
* MOBILE_CREDENTIAL_IACA_CREATE_FAIL
### Request Body
### Responses
#### 201 - IACA created
```json
{
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICDjCCAbSgAwIBAgIKdeZsA5NPKimuAzAKBggqhkjOPQQDAjAiMSAwCQYDVQQG\r\nEwJOWjATBgNVBAMTDEV4YW1wbGUgSUFDQTAeFw0yMzA5MTEyMzM0MjJaFw0zMzA5\r\nMDgyMzM0MjJaMCIxIDAJBgNVBAYTAk5aMBMGA1UEAxMMRXhhbXBsZSBJQUNBMFkw\r\nEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBbK7JKKFMWuu8kHQK2qaML+MQ0Ykk3Qg\r\n/p3TC6lQKvYJozPSpLXbJQIzMPq9u/dG+j4vq1iX/G/jFIwfiEiKEqOB0TCBzjAS\r\nBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIABjAdBgNVHQ4EFgQU9zTh\r\nKsqFxAgRJDDGW1au+ewJK6owHgYDVR0SBBcwFYYTaHR0cHM6Ly9leGFtcGxlLmNv\r\nbTBpBgNVHR8EYjBgMF6gXKBahlhodHRwczovL2V4YW1wbGUuY29tL3YyL2NyZWRl\r\nbnRpYWxzL21vYmlsZS9pYWNhcy8yZTg5YzE1Ni0zMWQ1LTQ3ODMtYmQ1OS05MDU1\r\nYjVmOGU3ZDIvY3JsMAoGCCqGSM49BAMCA0gAMEUCIQDD+eU8iOsYYC0v41L94fhF\r\nZ0brPo4gx2aRxrhE3NLFpwIgIgHCPBXJ+JICJg3K7dEsr153So4SEZzAA1rRn4eF\r\nvkM=\r\n-----END CERTIFICATE-----\r\n",
"certificateData": {
"notAfter": "2034-09-26",
"notBefore": "2023-09-26",
"country": "US",
"commonName": "{tenant-subdomain}.vii.mattr.global IACA",
"stateOrProvinceName": "US-AL"
},
"certificateFingerprint": "3c06145a53e6c252091a71540f870d4d521dede9f176a681a74e38ddc47bb311",
"isManaged": true
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 409 - Maximum number of IACA certificates reached. Please delete an existing certificate before creating a new one.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve an IACA
## Endpoint
```
GET /v2/credentials/mobile/iacas/{iacaId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/iacas/{iacaId}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves an existing IACA by providing its ID.
### **Analytic events**
* MOBILE_CREDENTIAL_IACA_RETRIEVE_START
* MOBILE_CREDENTIAL_IACA_RETRIEVE_SUCCESS
* MOBILE_CREDENTIAL_IACA_RETRIEVE_FAIL
### Path Parameters
- `iacaId`: string (required)
IACA ID
### Responses
#### 200 - IACA retrieved
```json
{
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICDjCCAbSgAwIBAgIKdeZsA5NPKimuAzAKBggqhkjOPQQDAjAiMSAwCQYDVQQG\r\nEwJOWjATBgNVBAMTDEV4YW1wbGUgSUFDQTAeFw0yMzA5MTEyMzM0MjJaFw0zMzA5\r\nMDgyMzM0MjJaMCIxIDAJBgNVBAYTAk5aMBMGA1UEAxMMRXhhbXBsZSBJQUNBMFkw\r\nEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBbK7JKKFMWuu8kHQK2qaML+MQ0Ykk3Qg\r\n/p3TC6lQKvYJozPSpLXbJQIzMPq9u/dG+j4vq1iX/G/jFIwfiEiKEqOB0TCBzjAS\r\nBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIABjAdBgNVHQ4EFgQU9zTh\r\nKsqFxAgRJDDGW1au+ewJK6owHgYDVR0SBBcwFYYTaHR0cHM6Ly9leGFtcGxlLmNv\r\nbTBpBgNVHR8EYjBgMF6gXKBahlhodHRwczovL2V4YW1wbGUuY29tL3YyL2NyZWRl\r\nbnRpYWxzL21vYmlsZS9pYWNhcy8yZTg5YzE1Ni0zMWQ1LTQ3ODMtYmQ1OS05MDU1\r\nYjVmOGU3ZDIvY3JsMAoGCCqGSM49BAMCA0gAMEUCIQDD+eU8iOsYYC0v41L94fhF\r\nZ0brPo4gx2aRxrhE3NLFpwIgIgHCPBXJ+JICJg3K7dEsr153So4SEZzAA1rRn4eF\r\nvkM=\r\n-----END CERTIFICATE-----\r\n",
"certificateData": {
"notAfter": "2034-09-26",
"notBefore": "2023-09-26",
"country": "US",
"commonName": "{tenant-subdomain}.vii.mattr.global IACA",
"stateOrProvinceName": "US-AL"
},
"certificateFingerprint": "3c06145a53e6c252091a71540f870d4d521dede9f176a681a74e38ddc47bb311",
"isManaged": true
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete an IACA
## Endpoint
```
DELETE /v2/credentials/mobile/iacas/{iacaId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/iacas/{iacaId}`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Deletes an existing IACA by providing its ID.
### **Analytic events**
* MOBILE_CREDENTIAL_IACA_DELETE_START
* MOBILE_CREDENTIAL_IACA_DELETE_LIST_SUCCESS
* MOBILE_CREDENTIAL_IACA_DELETE_LIST_FAIL
### Path Parameters
- `iacaId`: string (required)
IACA ID
### Responses
#### 204 - IACA deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update an IACA
## Endpoint
```
PUT /v2/credentials/mobile/iacas/{iacaId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/iacas/{iacaId}`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Update the status of an IACA. Only active IACAs can be used for signing mDocs.
Creating an IACA with `active` set to `false` enables distributing the IACA's PEM to relying parties in advance, before it is being used to sign any mDocs.
### **Analytic events**
* MOBILE_CREDENTIAL_IACA_UPDATE_START
* MOBILE_CREDENTIAL_IACA_UPDATE_SUCCESS
* MOBILE_CREDENTIAL_IACA_UPDATE_FAIL
### Path Parameters
- `iacaId`: string (required)
IACA ID
### Request Body
```json
{
"active": false
}
```
### Responses
#### 200 - IACA updated
```json
{
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICDjCCAbSgAwIBAgIKdeZsA5NPKimuAzAKBggqhkjOPQQDAjAiMSAwCQYDVQQG\r\nEwJOWjATBgNVBAMTDEV4YW1wbGUgSUFDQTAeFw0yMzA5MTEyMzM0MjJaFw0zMzA5\r\nMDgyMzM0MjJaMCIxIDAJBgNVBAYTAk5aMBMGA1UEAxMMRXhhbXBsZSBJQUNBMFkw\r\nEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBbK7JKKFMWuu8kHQK2qaML+MQ0Ykk3Qg\r\n/p3TC6lQKvYJozPSpLXbJQIzMPq9u/dG+j4vq1iX/G/jFIwfiEiKEqOB0TCBzjAS\r\nBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIABjAdBgNVHQ4EFgQU9zTh\r\nKsqFxAgRJDDGW1au+ewJK6owHgYDVR0SBBcwFYYTaHR0cHM6Ly9leGFtcGxlLmNv\r\nbTBpBgNVHR8EYjBgMF6gXKBahlhodHRwczovL2V4YW1wbGUuY29tL3YyL2NyZWRl\r\nbnRpYWxzL21vYmlsZS9pYWNhcy8yZTg5YzE1Ni0zMWQ1LTQ3ODMtYmQ1OS05MDU1\r\nYjVmOGU3ZDIvY3JsMAoGCCqGSM49BAMCA0gAMEUCIQDD+eU8iOsYYC0v41L94fhF\r\nZ0brPo4gx2aRxrhE3NLFpwIgIgHCPBXJ+JICJg3K7dEsr153So4SEZzAA1rRn4eF\r\nvkM=\r\n-----END CERTIFICATE-----\r\n",
"certificateData": {
"notAfter": "2034-09-26",
"notBefore": "2023-09-26",
"country": "US",
"commonName": "{tenant-subdomain}.vii.mattr.global IACA",
"stateOrProvinceName": "US-AL"
},
"certificateFingerprint": "3c06145a53e6c252091a71540f870d4d521dede9f176a681a74e38ddc47bb311",
"isManaged": true
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve IACA CRL
## Endpoint
```
GET /v2/credentials/mobile/iacas/{iacaId}/crl
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/iacas/{iacaId}/crl`
### Authorization
None required.
## Description
Retrieves the Certificate Revocation List (CRL) for the specified IACA in DER binary format.
This endpoint is public and does not require authentication. CRLs must be publicly accessible so relying parties can validate certificates.
### **Analytic events**
* MOBILE_CREDENTIAL_IACA_CRL_RETRIEVE_START
* MOBILE_CREDENTIAL_IACA_CRL_RETRIEVE_SUCCESS
* MOBILE_CREDENTIAL_IACA_CRL_RETRIEVE_FAIL
### Path Parameters
- `iacaId`: string (required)
IACA identifier
### Responses
#### 200 - IACA CRL retrieved
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all Document Signers
## Endpoint
```
GET /v2/credentials/mobile/document-signers
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/document-signers`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves all existing Document Signers from the tenant.
### **Analytic events**
* MOBILE_CREDENTIAL_DOCUMENT_SIGNER_RETRIEVE_LIST_START
* MOBILE_CREDENTIAL_DOCUMENT_SIGNER_RETRIEVE_LIST_SUCCESS
* MOBILE_CREDENTIAL_DOCUMENT_SIGNER_RETRIEVE_LIST_FAIL
### Responses
#### 200 - Document Signers retrieved
# Create a Document Signer
## Endpoint
```
POST /v2/credentials/mobile/document-signers
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/document-signers`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Creates a new Document Signer that can be used to sign new mDocs.
- Only available in implementations using unmanaged (external) IACAs.
- A maximum of five Document Signers can be created per tenant.
### **Analytic events**
* MOBILE_CREDENTIAL_DOCUMENT_SIGNER_CREATE_START
* MOBILE_CREDENTIAL_DOCUMENT_SIGNER_CREATE_SUCCESS
* MOBILE_CREDENTIAL_DOCUMENT_SIGNER_CREATE_FAIL
### Request Body
### Responses
#### 201 - Document Signer created
```json
{
"csrPem": "-----BEGIN CERTIFICATE REQUEST-----...-----END CERTIFICATE REQUEST-----"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 409 - Maximum number of document signer certificates reached. Please delete an existing certificate before creating a new one.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a Document Signer
## Endpoint
```
GET /v2/credentials/mobile/document-signers/{documentSignerId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/document-signers/{documentSignerId}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves an existing Document Signer by providing its ID.
### **Analytic events**
* MOBILE_CREDENTIAL_DOCUMENT_SIGNER_RETRIEVE_START
* MOBILE_CREDENTIAL_DOCUMENT_SIGNER_RETRIEVE_SUCCESS
* MOBILE_CREDENTIAL_DOCUMENT_SIGNER_RETRIEVE_FAIL
### Path Parameters
- `documentSignerId`: string (required)
Document Signer ID
### Responses
#### 200 - Document Signer retrieved
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a Document Signer
## Endpoint
```
DELETE /v2/credentials/mobile/document-signers/{documentSignerId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/document-signers/{documentSignerId}`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Deletes an existing Document Signer by providing its ID.
Only available in implementations using unmanaged (external) IACAs.
### **Analytic events**
* MOBILE_CREDENTIAL_DOCUMENT_SIGNER_DELETE_START
* MOBILE_CREDENTIAL_DOCUMENT_SIGNER_DELETE_SUCCESS
* MOBILE_CREDENTIAL_DOCUMENT_SIGNER_DELETE_FAIL
### Path Parameters
- `documentSignerId`: string (required)
Document Signer ID
### Responses
#### 204 - Document Signer deleted
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a Document Signer
## Endpoint
```
PUT /v2/credentials/mobile/document-signers/{documentSignerId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/document-signers/{documentSignerId}`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Updates an existing Document Signer by providing its ID and `active` parameter.
Only available in implementations using unmanaged (external) IACAs.
### **Analytic events**
* MOBILE_CREDENTIAL_DOCUMENT_SIGNER_UPDATE_START
* MOBILE_CREDENTIAL_DOCUMENT_SIGNER_UPDATE_SUCCESS
* MOBILE_CREDENTIAL_DOCUMENT_SIGNER_UPDATE_FAIL
### Path Parameters
- `documentSignerId`: string (required)
Document Signer ID
### Request Body
```json
{
"certificatePem": "-----BEGIN CERTIFICATE-----\\r\\nMIICbzCCAhSgAwIBAgIKfS7sskyJEh+DOzAKBggqhkjOPQQDAjAiMSAwCQYDVQQG\\r\\nEwJOWjATBgNVBAMTDEV4YW1wbGUgSUFDQTAeFw0yMzA5MTEyMzM0MjJaFw0yNDA5\\r\\nMTAyMzM0MjJaMDExLzAJBgNVBAYTAk5aMCIGA1UEAxMbZXhhbXBsZS5jb20gRG9j\\r\\ndW1lbnQgU2lnbmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7fa+jv9zCtHQ\\r\\nmKn7o1dS6lBHD5thlhPqjlx7qEfqy8Im9AcQJDal2sr/fUxhHwf/G4ublS7AL04U\\r\\n73dzr/ozxaOCASEwggEdMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFLdNNPTmPxt0\\r\\nLqvlZnV/QL86MXOxMB8GA1UdIwQYMBaAFPc04SrKhcQIESQwxltWrvnsCSuqMA4G\\r\\nA1UdDwEB/wQEAwIAgDAeBgNVHREEFzAVhhNodHRwczovL2V4YW1wbGUuY29tMB4G\\r\\nA1UdEgQXMBWGE2h0dHBzOi8vZXhhbXBsZS5jb20waQYDVR0fBGIwYDBeoFygWoZY\\r\\naHR0cHM6Ly9leGFtcGxlLmNvbS92Mi9jcmVkZW50aWFscy9tb2JpbGUvaWFjYXMv\\r\\nMmU4OWMxNTYtMzFkNS00NzgzLWJkNTktOTA1NWI1ZjhlN2QyL2NybDASBgNVHSUE\\r\\nCzAJBgcogYxdBQECMAoGCCqGSM49BAMCA0kAMEYCIQCfgn6+QoNfDVelJANl+Jp9\\r\\ncq7X9paZylfnI6UGr1FM6gIhAIzhiyclDa8+/ZSRfu7KfgGrNRaJ8YQ6vevskJls\\r\\nIavC\\r\\n-----END CERTIFICATE-----\\r\\n"
}
```
### Responses
#### 200 - Document Signer updated
```json
{
"certificatePem": "-----BEGIN CERTIFICATE-----\\r\\nMIICbzCCAhSgAwIBAgIKfS7sskyJEh+DOzAKBggqhkjOPQQDAjAiMSAwCQYDVQQG\\r\\nEwJOWjATBgNVBAMTDEV4YW1wbGUgSUFDQTAeFw0yMzA5MTEyMzM0MjJaFw0yNDA5\\r\\nMTAyMzM0MjJaMDExLzAJBgNVBAYTAk5aMCIGA1UEAxMbZXhhbXBsZS5jb20gRG9j\\r\\ndW1lbnQgU2lnbmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7fa+jv9zCtHQ\\r\\nmKn7o1dS6lBHD5thlhPqjlx7qEfqy8Im9AcQJDal2sr/fUxhHwf/G4ublS7AL04U\\r\\n73dzr/ozxaOCASEwggEdMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFLdNNPTmPxt0\\r\\nLqvlZnV/QL86MXOxMB8GA1UdIwQYMBaAFPc04SrKhcQIESQwxltWrvnsCSuqMA4G\\r\\nA1UdDwEB/wQEAwIAgDAeBgNVHREEFzAVhhNodHRwczovL2V4YW1wbGUuY29tMB4G\\r\\nA1UdEgQXMBWGE2h0dHBzOi8vZXhhbXBsZS5jb20waQYDVR0fBGIwYDBeoFygWoZY\\r\\naHR0cHM6Ly9leGFtcGxlLmNvbS92Mi9jcmVkZW50aWFscy9tb2JpbGUvaWFjYXMv\\r\\nMmU4OWMxNTYtMzFkNS00NzgzLWJkNTktOTA1NWI1ZjhlN2QyL2NybDASBgNVHSUE\\r\\nCzAJBgcogYxdBQECMAoGCCqGSM49BAMCA0kAMEYCIQCfgn6+QoNfDVelJANl+Jp9\\r\\ncq7X9paZylfnI6UGr1FM6gIhAIzhiyclDa8+/ZSRfu7KfgGrNRaJ8YQ6vevskJls\\r\\nIavC\\r\\n-----END CERTIFICATE-----\\r\\n",
"certificateFingerprint": "f6cad6e579d70b3973efa60624af731a580d1a11a7579e70f2f10f059dc86172",
"certificateData": {
"notAfter": "2034-09-26",
"notBefore": "2023-09-30",
"country": "US",
"stateOrProvinceName": "US-AL",
"commonName": "{tenant-subdomain}.vii.mattr.global Document Signer"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Revoke a Document Signer
## Endpoint
```
POST /v2/credentials/mobile/document-signers/{documentSignerId}/revoke
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/document-signers/{documentSignerId}/revoke`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Revokes an existing Document Signer, making it unusable for signing new mDocs.
If the verifier checks the CRL referenced in the IACA certificate, it must treat revoked Document Signers and any mDocs they signed as untrusted.
Only available in implementations using managed IACAs. When using unmanaged (external) IACAs, you must revoke the Document Signer certificate directly with the CA that issued it.
### **Analytic events**
* MOBILE_CREDENTIAL_DOCUMENT_SIGNER_REVOKE_START
* MOBILE_CREDENTIAL_DOCUMENT_SIGNER_REVOKE_SUCCESS
* MOBILE_CREDENTIAL_DOCUMENT_SIGNER_REVOKE_FAIL
### Path Parameters
- `documentSignerId`: string (required)
Document Signer identifier
### Request Body
### Responses
#### 200 - Document Signer revoked
```json
{
"revoked": true,
"revocationDate": "2025-10-31T23:59:59Z"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 409 - Document Signer already revoked
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all status list signers
## Endpoint
```
GET /v2/credentials/mobile/status-list-signers
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/status-list-signers`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves all existing status list signers.
### **Analytic events**
* MOBILE_CREDENTIAL_STATUS_LIST_SIGNER_RETRIEVE_LIST_START
* MOBILE_CREDENTIAL_STATUS_LIST_SIGNER_RETRIEVE_LIST_SUCCESS
* MOBILE_CREDENTIAL_STATUS_LIST_SIGNER_RETRIEVE_LIST_FAIL
### Responses
#### 200 - Status list signers retrieved
# Create a new status list signer
## Endpoint
```
POST /v2/credentials/mobile/status-list-signers
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/status-list-signers`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Uses an existing IACA to sign a status list signer (intermediate certificate) that can be used to sign status list tokens.
- Only available in implementations using unmanaged (external) IACAs.
- A maximum of three Status List Signers can be created per tenant.
### **Analytic events**
* MOBILE_CREDENTIAL_STATUS_LIST_SIGNER_CREATE_START
* MOBILE_CREDENTIAL_STATUS_LIST_SIGNER_CREATE_SUCCESS
* MOBILE_CREDENTIAL_STATUS_LIST_SIGNER_CREATE_FAIL
### Request Body
### Responses
#### 200 - Status list signer created
#### 409 - Maximum number of status list signer certificates reached. Please delete an existing certificate before creating a new one.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a status list signer
## Endpoint
```
GET /v2/credentials/mobile/status-list-signers/{statusListSignerId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/status-list-signers/{statusListSignerId}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves an existing status list signer.
Status list signer operations are only available in implementations using unmanaged (external) IACAs.
### **Analytic events**
* MOBILE_CREDENTIAL_STATUS_LIST_SIGNER_RETRIEVE_START
* MOBILE_CREDENTIAL_STATUS_LIST_SIGNER_RETRIEVE_SUCCESS
* MOBILE_CREDENTIAL_STATUS_LIST_SIGNER_RETRIEVE_FAIL
### Path Parameters
- `statusListSignerId`: string (required)
Status list identifier
### Responses
#### 200 - Status list signer retrieved
# Delete a status list signer
## Endpoint
```
DELETE /v2/credentials/mobile/status-list-signers/{statusListSignerId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/status-list-signers/{statusListSignerId}`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Deletes an existing status list signer.
Only available in implementations using unmanaged (external) IACAs.
### **Analytic events**
* MOBILE_CREDENTIAL_STATUS_LIST_SIGNER_DELETE_START
* MOBILE_CREDENTIAL_STATUS_LIST_SIGNER_DELETE_SUCCESS
* MOBILE_CREDENTIAL_STATUS_LIST_SIGNER_DELETE_FAIL
### Path Parameters
- `statusListSignerId`: string (required)
Status list identifier
### Responses
#### 204 - No Content
# Update a status list signer
## Endpoint
```
PUT /v2/credentials/mobile/status-list-signers/{statusListSignerId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/status-list-signers/{statusListSignerId}`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Updates the status of an existing status list signer.
Only available in implementations using unmanaged (external) IACAs.
### **Analytic events**
* MOBILE_CREDENTIAL_STATUS_LIST_SIGNER_UPDATE_START
* MOBILE_CREDENTIAL_STATUS_LIST_SIGNER_UPDATE_SUCCESS
* MOBILE_CREDENTIAL_STATUS_LIST_SIGNER_UPDATE_FAIL
### Path Parameters
- `statusListSignerId`: string (required)
Status list identifier
### Request Body
### Responses
#### 200 - Status list signer updated
```json
{
"active": true,
"certificateFingerprint": "475DA948E4BA44D9B5BC31AB4B8006113FD5F538"
}
```
# Revoke a status list signer
## Endpoint
```
POST /v2/credentials/mobile/status-list-signers/{statusListSignerId}/revoke
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/status-list-signers/{statusListSignerId}/revoke`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Revokes an existing status list signer, making it unusable for signing new status lists.
If the verifier checks the CRL referenced in the IACA certificate, it must treat revoked status list signers and any status list they signed as untrusted.
Only available in implementations using managed IACAs. When using unmanaged (external) IACAs, you must revoke the status list signer certificate directly with the CA that issued it.
### **Analytic events**
* MOBILE_CREDENTIAL_STATUS_LIST_SIGNER_REVOKE_START
* MOBILE_CREDENTIAL_STATUS_LIST_SIGNER_REVOKE_SUCCESS
* MOBILE_CREDENTIAL_STATUS_LIST_SIGNER_REVOKE_FAIL
### Path Parameters
- `statusListSignerId`: string (required)
Status list signer identifier
### Request Body
### Responses
#### 200 - Status list signer revoked
```json
{
"revoked": true,
"revocationDate": "2025-10-31T23:59:59Z"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 409 - Status list signer already revoked
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete mDoc metadata
## Endpoint
```
DELETE /v2/credentials/mobile/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Deletes all stored data for an existing mDoc that matches the provided ID.
Removed credential data cannot be recovered.
### **Analytic events**
* USER_CREDENTIAL_DELETE_START
* USER_CREDENTIAL_DELETE_SUCCESS
* USER_CREDENTIAL_DELETE_FAIL
### Responses
#### 204 - mDoc metadata deleted
#### 400 - Invalid id parameter format
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all mDocs configurations
## Endpoint
```
GET /v2/credentials/mobile/configurations
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/configurations`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves all mDocs configurations from your tenant.
### **Analytic events**
* MOBILE_CREDENTIAL_CONFIGURATION_RETRIEVE_LIST_START
* MOBILE_CREDENTIAL_CONFIGURATION_RETRIEVE_LIST_SUCCESS
* MOBILE_CREDENTIAL_CONFIGURATION_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
- `type`: string
Optional credential type to filter on
### Responses
#### 200 - mDocs configurations retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM",
"data": [
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"branding": {
"name": "Credential name",
"description": "Credential Description",
"backgroundColor": "#FFFFFF",
"watermarkImage": "data:image/png;base64,{image-data}",
"issuerLogo": "data:image/png;base64,{image-data}",
"issuerIcon": "data:image/svg+xml;base64,{image-data}"
},
"includeStatus": true,
"type": "org.iso.18013.5.1.mDL",
"claimMappings": {
"org.iso.18013.5.1": {
"given_name": {
"mapFrom": "claims.given_name",
"required": true,
"type": "string",
"display": [
{
"name": "Given Name",
"locale": "en-US"
},
{
"name": "Vorname",
"locale": "de-DE"
}
]
},
"birth_date": {
"mapFrom": "claims.date_of_birth",
"required": true,
"type": "dateTime"
}
}
},
"expiresIn": {
"months": 1
}
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create an mDocs configuration
## Endpoint
```
POST /v2/credentials/mobile/configurations
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/configurations`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Creates a new mDocs configuration, a specific set of rules and parameters that are used to create and validate a particular type of verifiable credential. These rules and parameters define how the credential is structured and what data it contains when issued.
### **Analytic events**
* MOBILE_CREDENTIAL_CONFIGURATION_CREATE_START
* MOBILE_CREDENTIAL_CONFIGURATION_CREATE_SUCCESS
* MOBILE_CREDENTIAL_CONFIGURATION_CREATE_FAIL
### Request Body
The mDocs configuration payload
```json
{
"type": "org.iso.18013.5.1.mDL",
"claimMappings": {
"org.iso.18013.5.1": {
"given_name": {
"mapFrom": "claims.given_name",
"required": true,
"type": "string",
"display": [
{
"name": "Given Name",
"locale": "en-US"
},
{
"name": "Vorname",
"locale": "de-DE"
}
]
},
"birth_date": {
"mapFrom": "claims.date_of_birth",
"required": true,
"type": "dateTime"
}
}
},
"expiresIn": {
"months": 1
},
"branding": {
"name": "Credential name",
"description": "Credential Description",
"backgroundColor": "#FFFFFF",
"watermarkImage": "data:image/png;base64,{image-data}",
"issuerLogo": "https://example-path-to-image-data.com",
"issuerIcon": "data:image/svg+xml;base64,{image-data}"
},
"includeStatus": true
}
```
### Responses
#### 201 - mDocs configuration created
```json
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"branding": {
"name": "Credential name",
"description": "Credential Description",
"backgroundColor": "#FFFFFF",
"watermarkImage": "data:image/png;base64,{image-data}",
"issuerLogo": "data:image/png;base64,{image-data}",
"issuerIcon": "data:image/svg+xml;base64,{image-data}"
},
"includeStatus": true,
"type": "org.iso.18013.5.1.mDL",
"claimMappings": {
"org.iso.18013.5.1": {
"given_name": {
"mapFrom": "claims.given_name",
"required": true,
"type": "string",
"display": [
{
"name": "Given Name",
"locale": "en-US"
},
{
"name": "Vorname",
"locale": "de-DE"
}
]
},
"birth_date": {
"mapFrom": "claims.date_of_birth",
"required": true,
"type": "dateTime"
}
}
},
"expiresIn": {
"months": 1
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve an mDocs configuration
## Endpoint
```
GET /v2/credentials/mobile/configurations/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/configurations/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves an existing mDocs configuration by providing its ID.
### **Analytic events**
* MOBILE_CREDENTIAL_CONFIGURATION_RETRIEVE_START
* MOBILE_CREDENTIAL_CONFIGURATION_RETRIEVE_SUCCESS
* MOBILE_CREDENTIAL_CONFIGURATION_RETRIEVE_FAIL
### Responses
#### 200 - mDocs configuration retrieved
```json
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"branding": {
"name": "Credential name",
"description": "Credential Description",
"backgroundColor": "#FFFFFF",
"watermarkImage": "data:image/png;base64,{image-data}",
"issuerLogo": "data:image/png;base64,{image-data}",
"issuerIcon": "data:image/svg+xml;base64,{image-data}"
},
"includeStatus": true,
"type": "org.iso.18013.5.1.mDL",
"claimMappings": {
"org.iso.18013.5.1": {
"given_name": {
"mapFrom": "claims.given_name",
"required": true,
"type": "string",
"display": [
{
"name": "Given Name",
"locale": "en-US"
},
{
"name": "Vorname",
"locale": "de-DE"
}
]
},
"birth_date": {
"mapFrom": "claims.date_of_birth",
"required": true,
"type": "dateTime"
}
}
},
"expiresIn": {
"months": 1
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete an mDocs configuration
## Endpoint
```
DELETE /v2/credentials/mobile/configurations/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/configurations/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Deletes an existing mDocs configuration by providing its ID.
### **Analytic events**
* MOBILE_CREDENTIAL_CONFIGURATION_DELETE_START
* MOBILE_CREDENTIAL_CONFIGURATION_DELETE_SUCCESS
* MOBILE_CREDENTIAL_CONFIGURATION_DELETE_FAIL
### Responses
#### 204 - mDocs configuration deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update an mDocs configuration
## Endpoint
```
PUT /v2/credentials/mobile/configurations/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/configurations/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Updates an existing mDocs configuration by providing its ID.
### **Analytic events**
* MOBILE_CREDENTIAL_CONFIGURATION_UPDATE_START
* MOBILE_CREDENTIAL_CONFIGURATION_UPDATE_SUCCESS
* MOBILE_CREDENTIAL_CONFIGURATION_UPDATE_FAIL
### Request Body
Update an mDocs configuration
```json
{
"type": "org.iso.18013.5.1.mDL",
"claimMappings": {
"org.iso.18013.5.1": {
"given_name": {
"mapFrom": "claims.given_name",
"required": true,
"type": "string",
"display": [
{
"name": "Given Name",
"locale": "en-US"
},
{
"name": "Vorname",
"locale": "de-DE"
}
]
},
"birth_date": {
"mapFrom": "claims.date_of_birth",
"required": true,
"type": "dateTime"
}
}
},
"expiresIn": {
"months": 1
},
"branding": {
"name": "Credential name",
"description": "Credential Description",
"backgroundColor": "#FFFFFF",
"watermarkImage": "data:image/png;base64,{image-data}",
"issuerLogo": "https://example-path-to-image-data.com",
"issuerIcon": "data:image/svg+xml;base64,{image-data}"
},
"includeStatus": true
}
```
### Responses
#### 200 - mDocs configuration updated
```json
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"branding": {
"name": "Credential name",
"description": "Credential Description",
"backgroundColor": "#FFFFFF",
"watermarkImage": "data:image/png;base64,{image-data}",
"issuerLogo": "data:image/png;base64,{image-data}",
"issuerIcon": "data:image/svg+xml;base64,{image-data}"
},
"includeStatus": true,
"type": "org.iso.18013.5.1.mDL",
"claimMappings": {
"org.iso.18013.5.1": {
"given_name": {
"mapFrom": "claims.given_name",
"required": true,
"type": "string",
"display": [
{
"name": "Given Name",
"locale": "en-US"
},
{
"name": "Vorname",
"locale": "de-DE"
}
]
},
"birth_date": {
"mapFrom": "claims.date_of_birth",
"required": true,
"type": "dateTime"
}
}
},
"expiresIn": {
"months": 1
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve mDoc status
## Endpoint
```
GET /v2/credentials/mobile/{credentialId}/status
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/{credentialId}/status`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves the status of an existing mDoc by providing its `credentialId`.
### **Analytic events**
* MOBILE_CREDENTIAL_STATUS_RETRIEVE_START
* MOBILE_CREDENTIAL_STATUS_RETRIEVE_SUCCESS
* MOBILE_CREDENTIAL_STATUS_RETRIEVE_FAIL
### Path Parameters
- `credentialId`: string (required)
mDoc identifier
### Responses
#### 200 - Credential status retrieved
```json
{
"status": "valid"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update mDoc status
## Endpoint
```
POST /v2/credentials/mobile/{credentialId}/status
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/{credentialId}/status`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Sets the status of an existing mDoc by providing its `credentialId` and the new status.
Available status values depend on the Status List configuration format:
**Draft 14 of the IETF Token Status List specification (1-bit encoding)**:
* **valid** - Credential is valid
* **invalid** - Credential is invalid (cannot be reversed)
**Deprecated Legacy format (2-bit encoding)**:
* **valid** - Credential is valid
* **invalid** - Credential is invalid (cannot be reversed)
* **suspended** - Credential is temporarily suspended
The **suspended** status is deprecated and only available in legacy format.
### **Analytic events**
* MOBILE_CREDENTIAL_STATUS_SET_START
* MOBILE_CREDENTIAL_STATUS_SET_SUCCESS
* MOBILE_CREDENTIAL_STATUS_SET_FAIL
### Request Body
Credential status payload
```json
{
"status": "valid"
}
```
### Responses
#### 201 - Credential status updated
```json
{
"status": "valid"
}
```
#### 400 - Bad Request
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all Status list configurations
## Endpoint
```
GET /v2/credentials/mobile/status-lists/configurations
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/status-lists/configurations`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves all Status list configurations from your tenant.
### **Analytic events**
* MOBILE_CREDENTIAL_STATUS_LIST_CONFIGURATION_RETRIEVE_LIST_START
* MOBILE_CREDENTIAL_STATUS_LIST_CONFIGURATION_RETRIEVE_LIST_
* MOBILE_CREDENTIAL_STATUS_LIST_CONFIGURATION_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Status list configurations retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM",
"data": [
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"docType": "Drivers License",
"timeToLiveDuration": {
"days": 1
},
"expiryDuration": {
"days": 2
}
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create a Status list configuration
## Endpoint
```
POST /v2/credentials/mobile/status-lists/configurations
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/status-lists/configurations`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Creates a Status list configuration, which defines a status list validity periods. mDocs can then be assigned to a specific Status list configuration.
### **Analytic events**
* MOBILE_CREDENTIAL_STATUS_LIST_CONFIGURATION_CREATE_START
* MOBILE_CREDENTIAL_STATUS_LIST_CONFIGURATION_CREATE_SUCCESS
* MOBILE_CREDENTIAL_STATUS_LIST_CONFIGURATION_CREATE_FAIL
### Request Body
The Status list configuration payload
```json
{
"docType": "DriverLicense",
"timeToLiveDuration": {
"days": 1
},
"expiryDuration": {
"days": 2
}
}
```
### Responses
#### 201 - Status list configuration created
```json
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"docType": "Drivers License",
"timeToLiveDuration": {
"days": 1
},
"expiryDuration": {
"days": 2
}
}
```
#### 400 - Bad Request
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a Status list configuration
## Endpoint
```
GET /v2/credentials/mobile/status-lists/configurations/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/status-lists/configurations/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves an existing Status list configuration by providing its ID.
### **Analytic events**
* MOBILE_CREDENTIAL_STATUS_LIST_CONFIGURATION_RETRIEVE_START
* MOBILE_CREDENTIAL_STATUS_LIST_CONFIGURATION_RETRIEVE_SUCCESS
* MOBILE_CREDENTIAL_STATUS_LIST_CONFIGURATION_RETRIEVE_FAIL
### Path Parameters
- `id`: string (required)
Status list configuration ID
### Responses
#### 200 - Status list configuration retrieved
```json
{
"docType": "DriverLicense",
"timeToLiveDuration": {
"days": 1
},
"expiryDuration": {
"days": 2
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a Status list configuration
## Endpoint
```
DELETE /v2/credentials/mobile/status-lists/configurations/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/status-lists/configurations/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Permanently deletes an existing Status list configuration.
### **Analytic events**
* MOBILE_CREDENTIAL_STATUS_LIST_CONFIGURATION_DELETE_START
* MOBILE_CREDENTIAL_STATUS_LIST_CONFIGURATION_DELETE_SUCCESS
* MOBILE_CREDENTIAL_STATUS_LIST_CONFIGURATION_DELETE_FAIL
### Path Parameters
- `id`: string (required)
Status list configuration ID
### Responses
#### 204 - Status list configuration deleted
#### 400 - Bad request
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a Status list configuration
## Endpoint
```
PUT /v2/credentials/mobile/status-lists/configurations/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/status-lists/configurations/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Updates an existing Status list configuration, allowing you to adjust the expiry and TTL (Time To Live) settings.
### **Analytic events**
* MOBILE_CREDENTIAL_STATUS_LIST_CONFIGURATION_UPDATE_START
* MOBILE_CREDENTIAL_STATUS_LIST_CONFIGURATION_UPDATE_SUCCESS
* MOBILE_CREDENTIAL_STATUS_LIST_CONFIGURATION_UPDATE_FAIL
### Path Parameters
- `id`: string (required)
Status list configuration ID
### Request Body
The Status list configuration payload
```json
{
"timeToLiveDuration": {
"days": 1
},
"expiryDuration": {
"days": 2
}
}
```
### Responses
#### 200 - Status list configuration updated
```json
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"docType": "Drivers License",
"timeToLiveDuration": {
"days": 1
},
"expiryDuration": {
"days": 2
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all Status lists
## Endpoint
```
GET /v2/credentials/mobile/status-lists
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/status-lists`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
**Deprecated.** In line with our SLA, this endpoint will be removed no earlier than 22 September 2026. To discover the Status lists available on a tenant, use the public [Status list distribution](#operation/getStatusListDistribution) endpoint (`GET /v2/credentials/mobile/status-lists/distribution`), which returns the URLs of all Status list tokens on the tenant. To retrieve an individual Status list token, use the public [Retrieve a Status list token](#operation/getStatusListToken) endpoint (`GET /v2/credentials/mobile/status-lists/{statusListId}/token`).
Retrieves all existing status lists from your tenant.
### **Analytic events**
* MOBILE_CREDENTIAL_STATUS_LIST_RETRIEVE_LIST_START
* MOBILE_CREDENTIAL_STATUS_LIST_RETRIEVE_LIST_SUCCESS
* MOBILE_CREDENTIAL_STATUS_LIST_RETRIEVE_LIST_FAIL
### Responses
#### 200 - Status lists retrieved
```json
{
"data": [
{
"listSize": "100_000",
"list": "0oRZAu6jEHRtYXR0ci1zdGF0dXNs..."
}
]
}
```
# Retrieve a Status list
## Endpoint
```
GET /v2/credentials/mobile/status-lists/{statusListId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/status-lists/{statusListId}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
**Deprecated.** In line with our SLA, this endpoint will be removed no earlier than 22 September 2026. To retrieve a Status list token, use the public [Retrieve a Status list token](#operation/getStatusListToken) endpoint (`GET /v2/credentials/mobile/status-lists/{statusListId}/token`), which returns the signed Status list token in CWT format. To discover the Status lists available on a tenant, use the public [Status list distribution](#operation/getStatusListDistribution) endpoint (`GET /v2/credentials/mobile/status-lists/distribution`).
Retrieves an existing Status list and its signed token by providing its ID.
### **Analytic events**
* MOBILE_CREDENTIAL_STATUS_LIST_RETRIEVE_START
* MOBILE_CREDENTIAL_STATUS_LIST_RETRIEVE_SUCCESS
* MOBILE_CREDENTIAL_STATUS_LIST_RETRIEVE_FAIL
### Path Parameters
- `statusListId`: string (required)
Status list unique identifier
### Responses
#### 200 - Status list retrieved
```json
{
"listSize": "100_000",
"list": "0oRZAu6jEHRtYXR0ci1zdGF0dXNs..."
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a Status list token
## Endpoint
```
GET /v2/credentials/mobile/status-lists/{statusListId}/token
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/status-lists/{statusListId}/token`
### Authorization
None required.
## Description
Retrieves the Status list token in CWT format. This public endpoint returns a token which contains a compressed, signed list of credential statuses. Relying parties can use this token to check the revocation status of an mDoc that references this Status list.
This endpoint is intended for public consumption, and as such does not require authentication.
**Token Format Differences**:
The token structure depends on the Status List configuration format:
**Draft 14 of the IETF Token Status List specification**:
- Token header `typ`: `application/statuslist+cwt`
- CBOR payload claims: `65533` (status_list), `65534` (ttl)
- Status encoding: 1-bit (Valid/Invalid)
**Legacy format**:
- Token header `typ`: `mattr-statuslist+cwt`
- CBOR payload claims: `-65538` (status_list), `-65539` (ttl)
- Status encoding: 2-bit (Valid/Invalid/Suspended)
### **Analytic events**
* MOBILE_CREDENTIAL_STATUS_LIST_TOKEN_RETRIEVE_START
* MOBILE_CREDENTIAL_STATUS_LIST_TOKEN_RETRIEVE_SUCCESS
* MOBILE_CREDENTIAL_STATUS_LIST_TOKEN_RETRIEVE_FAIL
### Path Parameters
- `statusListId`: string (required)
Status list identifier
### Responses
#### 200 - Status list token retrieved
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 410 - Status list expired
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Status list distribution
## Endpoint
```
GET /v2/credentials/mobile/status-lists/distribution
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/status-lists/distribution`
### Authorization
None required.
## Description
Retrieves an object that details all existing Status lists tokens on the tenant. This public endpoint allows a relying party to consume and cache status lists. Each list
in the response includes a URL where its token can be retrieved. Status list tokens that were signed by expired IACAs are excluded from the response.
This endpoint is intended for public consumption, and as such does not require authentication.
**Response Format Differences**:
The response structure depends on the Status List configuration format:
**Draft 14 of the IETF Token Status List specification**: `{"status_lists": ["https://..."]}`
**Legacy format**: `{"status_lists": [{"uri": "https://..."}]}`
### **Analytic events**
* MOBILE_CREDENTIAL_STATUS_LIST_DISTRIBUTION_START
* MOBILE_CREDENTIAL_STATUS_LIST_DISTRIBUTION_SUCCESS
* MOBILE_CREDENTIAL_STATUS_LIST_DISTRIBUTION_FAIL
### Responses
#### 200 - Status lists retrieved
# Retrieve all trusted issuers
## Endpoint
```
GET /v2/credentials/mobile/trusted-issuers
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/trusted-issuers`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Retrieves all mDocs trusted issuers from your tenant.
### **Analytic events**
* MOBILE_CREDENTIAL_TRUSTED_ISSUER_RETRIEVE_LIST_START
* MOBILE_CREDENTIAL_TRUSTED_ISSUER_RETRIEVE_LIST_SUCCESS
* MOBILE_CREDENTIAL_TRUSTED_ISSUER_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Trusted issuers retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM",
"data": [
{
"id": "ed74319e-72a6-4401-b3a5-94e980fbebea",
"certificatePem": "-----BEGIN CERTIFICATE-----\\r\\nMIICUDCCAfWgAwIBAgIKVVqBlVonWFs3lTAKBggqhkjOPQQDAjAkMQswCQYDVQQG\\r\\nEwJOWjEVMBMGA1UEAwwMRXhhbXBsZSBJQUNBMB4XDTI0MDExMTAzMjYwMFoXDTM0\\r\\nMDEwODAzMjYwMFowJDELMAkGA1UEBhMCTloxFTATBgNVBAMMDEV4YW1wbGUgSUFD\\r\\nQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOHxm9MYkCvIvZc/MyoWGul8+tla\\r\\nFSSRVkDllFERbO/Tg7DOj4CJfYrhDJEuV04eRgcowBDhr9W/bvnTMZMa/RijggEN\\r\\nMIIBCTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4E\\r\\nFgQUpS3hOCbmCUwu8n91X9CLS682cOkwOwYDVR0SBDQwMoYwaHR0cHM6Ly9odWRz\\r\\nb24tdGVuYW50LTAwMS52aWkuYXUzMDEubWF0dHJsYWJzLmlvMIGGBgNVHR8EfzB9\\r\\nMHugeaB3hnVodHRwczovL2h1ZHNvbi10ZW5hbnQtMDAxLnZpaS5hdTMwMS5tYXR0\\r\\ncmxhYnMuaW8vdjIvY3JlZGVudGlhbHMvbW9iaWxlL2lhY2FzL2VkNzQzMTllLTcy\\r\\nYTYtNDQwMS1iM2E1LTk0ZTk4MGZiZWJlYS9jcmwwCgYIKoZIzj0EAwIDSQAwRgIh\\r\\nAJxWGZvntq+hymL7zWwrlZo1Jz1+lWglu/MESdmUhTNFAiEAg+x5e3TzBxgHneIM\\r\\nVpTmZNOyZI3Hn17WRKkyKSg+5/8=\\r\\n-----END CERTIFICATE-----\\r\\n",
"certificateData": {
"notAfter": "2033-09-23",
"notBefore": "2023-09-23",
"country": "NZ",
"commonName": "Example Trusted Issuer"
}
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create a trusted issuer
## Endpoint
```
POST /v2/credentials/mobile/trusted-issuers
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/trusted-issuers`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Add a new mDocs trusted issuer, to be used in online presentation workflows.
### **Analytic events**
* MOBILE_CREDENTIAL_TRUSTED_ISSUER_CREATE_START
* MOBILE_CREDENTIAL_TRUSTED_ISSUER_CREATE_SUCCESS
* MOBILE_CREDENTIAL_TRUSTED_ISSUER_CREATE_FAIL
### Request Body
The trusted issuer payload
```json
{
"certificatePem": "-----BEGIN CERTIFICATE-----\\r\\nMIICUDCCAfWgAwIBAgIKVVqBlVonWFs3lTAKBggqhkjOPQQDAjAkMQswCQYDVQQG\\r\\nEwJOWjEVMBMGA1UEAwwMRXhhbXBsZSBJQUNBMB4XDTI0MDExMTAzMjYwMFoXDTM0\\r\\nMDEwODAzMjYwMFowJDELMAkGA1UEBhMCTloxFTATBgNVBAMMDEV4YW1wbGUgSUFD\\r\\nQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOHxm9MYkCvIvZc/MyoWGul8+tla\\r\\nFSSRVkDllFERbO/Tg7DOj4CJfYrhDJEuV04eRgcowBDhr9W/bvnTMZMa/RijggEN\\r\\nMIIBCTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4E\\r\\nFgQUpS3hOCbmCUwu8n91X9CLS682cOkwOwYDVR0SBDQwMoYwaHR0cHM6Ly9odWRz\\r\\nb24tdGVuYW50LTAwMS52aWkuYXUzMDEubWF0dHJsYWJzLmlvMIGGBgNVHR8EfzB9\\r\\nMHugeaB3hnVodHRwczovL2h1ZHNvbi10ZW5hbnQtMDAxLnZpaS5hdTMwMS5tYXR0\\r\\ncmxhYnMuaW8vdjIvY3JlZGVudGlhbHMvbW9iaWxlL2lhY2FzL2VkNzQzMTllLTcy\\r\\nYTYtNDQwMS1iM2E1LTk0ZTk4MGZiZWJlYS9jcmwwCgYIKoZIzj0EAwIDSQAwRgIh\\r\\nAJxWGZvntq+hymL7zWwrlZo1Jz1+lWglu/MESdmUhTNFAiEAg+x5e3TzBxgHneIM\\r\\nVpTmZNOyZI3Hn17WRKkyKSg+5/8=\\r\\n-----END CERTIFICATE-----\\r\\n"
}
```
### Responses
#### 201 - Trusted issuer created
```json
{
"id": "ed74319e-72a6-4401-b3a5-94e980fbebea",
"certificatePem": "-----BEGIN CERTIFICATE-----\\r\\nMIICUDCCAfWgAwIBAgIKVVqBlVonWFs3lTAKBggqhkjOPQQDAjAkMQswCQYDVQQG\\r\\nEwJOWjEVMBMGA1UEAwwMRXhhbXBsZSBJQUNBMB4XDTI0MDExMTAzMjYwMFoXDTM0\\r\\nMDEwODAzMjYwMFowJDELMAkGA1UEBhMCTloxFTATBgNVBAMMDEV4YW1wbGUgSUFD\\r\\nQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOHxm9MYkCvIvZc/MyoWGul8+tla\\r\\nFSSRVkDllFERbO/Tg7DOj4CJfYrhDJEuV04eRgcowBDhr9W/bvnTMZMa/RijggEN\\r\\nMIIBCTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4E\\r\\nFgQUpS3hOCbmCUwu8n91X9CLS682cOkwOwYDVR0SBDQwMoYwaHR0cHM6Ly9odWRz\\r\\nb24tdGVuYW50LTAwMS52aWkuYXUzMDEubWF0dHJsYWJzLmlvMIGGBgNVHR8EfzB9\\r\\nMHugeaB3hnVodHRwczovL2h1ZHNvbi10ZW5hbnQtMDAxLnZpaS5hdTMwMS5tYXR0\\r\\ncmxhYnMuaW8vdjIvY3JlZGVudGlhbHMvbW9iaWxlL2lhY2FzL2VkNzQzMTllLTcy\\r\\nYTYtNDQwMS1iM2E1LTk0ZTk4MGZiZWJlYS9jcmwwCgYIKoZIzj0EAwIDSQAwRgIh\\r\\nAJxWGZvntq+hymL7zWwrlZo1Jz1+lWglu/MESdmUhTNFAiEAg+x5e3TzBxgHneIM\\r\\nVpTmZNOyZI3Hn17WRKkyKSg+5/8=\\r\\n-----END CERTIFICATE-----\\r\\n",
"certificateData": {
"notAfter": "2033-09-23",
"notBefore": "2023-09-23",
"country": "NZ",
"commonName": "Example Trusted Issuer"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a trusted issuer
## Endpoint
```
GET /v2/credentials/mobile/trusted-issuers/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/trusted-issuers/{id}`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Retrieves an existing trusted issuer from your tenant by providing its ID.
### **Analytic events**
* MOBILE_CREDENTIAL_TRUSTED_ISSUER_RETRIEVE_START
* MOBILE_CREDENTIAL_TRUSTED_ISSUER_RETRIEVE_SUCCESS
* MOBILE_CREDENTIAL_TRUSTED_ISSUER_RETRIEVE_FAIL
### Responses
#### 200 - Trusted issuer retrieved
```json
{
"id": "ed74319e-72a6-4401-b3a5-94e980fbebea",
"certificatePem": "-----BEGIN CERTIFICATE-----\\r\\nMIICUDCCAfWgAwIBAgIKVVqBlVonWFs3lTAKBggqhkjOPQQDAjAkMQswCQYDVQQG\\r\\nEwJOWjEVMBMGA1UEAwwMRXhhbXBsZSBJQUNBMB4XDTI0MDExMTAzMjYwMFoXDTM0\\r\\nMDEwODAzMjYwMFowJDELMAkGA1UEBhMCTloxFTATBgNVBAMMDEV4YW1wbGUgSUFD\\r\\nQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOHxm9MYkCvIvZc/MyoWGul8+tla\\r\\nFSSRVkDllFERbO/Tg7DOj4CJfYrhDJEuV04eRgcowBDhr9W/bvnTMZMa/RijggEN\\r\\nMIIBCTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4E\\r\\nFgQUpS3hOCbmCUwu8n91X9CLS682cOkwOwYDVR0SBDQwMoYwaHR0cHM6Ly9odWRz\\r\\nb24tdGVuYW50LTAwMS52aWkuYXUzMDEubWF0dHJsYWJzLmlvMIGGBgNVHR8EfzB9\\r\\nMHugeaB3hnVodHRwczovL2h1ZHNvbi10ZW5hbnQtMDAxLnZpaS5hdTMwMS5tYXR0\\r\\ncmxhYnMuaW8vdjIvY3JlZGVudGlhbHMvbW9iaWxlL2lhY2FzL2VkNzQzMTllLTcy\\r\\nYTYtNDQwMS1iM2E1LTk0ZTk4MGZiZWJlYS9jcmwwCgYIKoZIzj0EAwIDSQAwRgIh\\r\\nAJxWGZvntq+hymL7zWwrlZo1Jz1+lWglu/MESdmUhTNFAiEAg+x5e3TzBxgHneIM\\r\\nVpTmZNOyZI3Hn17WRKkyKSg+5/8=\\r\\n-----END CERTIFICATE-----\\r\\n",
"certificateData": {
"notAfter": "2033-09-23",
"notBefore": "2023-09-23",
"country": "NZ",
"commonName": "Example Trusted Issuer"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a trusted issuer
## Endpoint
```
DELETE /v2/credentials/mobile/trusted-issuers/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/mobile/trusted-issuers/{id}`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Deletes an existing trusted issuer by providing its ID.
### **Analytic events**
* MOBILE_CREDENTIAL_TRUSTED_ISSUER_DELETE_START
* MOBILE_CREDENTIAL_TRUSTED_ISSUER_DELETE_SUCCESS
* MOBILE_CREDENTIAL_TRUSTED_ISSUER_DELETE_FAIL
### Responses
#### 204 - Trusted issuer deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all trusted issuers
## Endpoint
```
GET /v2/presentations/trusted-issuers
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/trusted-issuers`
### Authorization
None required.
## Description
Retrieves all configured trusted issuers.
This endpoint is intended for public consumption, and as such does not require authentication.
### **Analytic events**
* CREDENTIAL_PRESENTATION_TRUSTED_ISSUERS_RETRIEVE_LIST_START
* CREDENTIAL_PRESENTATION_TRUSTED_ISSUERS_RETRIEVE_LIST_SUCCESS
* CREDENTIAL_PRESENTATION_TRUSTED_ISSUERS_RETRIEVE_LIST_FAIL
### Responses
#### 200 - Trusted issuers retrieved
# Retrieve all verifier root CA certificates
## Endpoint
```
GET /v2/presentations/certificates/ca
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/certificates/ca`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Retrieves all existing verifier root CA certificates.
### **Analytic events**
* CREDENTIAL_PRESENTATION_VERIFIER_CA_CERTIFICATE_RETRIEVE_LIST_START
* CREDENTIAL_PRESENTATION_VERIFIER_CA_CERTIFICATE_RETRIEVE_LIST_SUCCESS
* CREDENTIAL_PRESENTATION_VERIFIER_CA_CERTIFICATE_RETRIEVE_LIST_FAIL
### Responses
#### 200 - Verifier root CA certificates retrieved
```json
{
"data": [
{
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"certificateFingerprint": "a3b2c1d4e5f60718293a4b5c6d7e8f90123456789abcdef0123456789abcdef0",
"certificateData": {
"commonName": "Example Verifier",
"country": "US",
"notAfter": "2024-10-22T00:00:00Z",
"notBefore": "2023-10-22T00:00:00Z"
}
}
]
}
```
# Create a verifier root CA certificate
## Endpoint
```
POST /v2/presentations/certificates/ca
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/certificates/ca`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Creates a verifier root CA certificate to be used as part of mDocs online verification workflows.
- A maximum of three Verifier root CA certificates can be created per tenant.
### **Analytic events**
* CREDENTIAL_PRESENTATION_VERIFIER_CA_CERTIFICATE_CREATE_START
* CREDENTIAL_PRESENTATION_VERIFIER_CA_CERTIFICATE_CREATE_SUCCESS
* CREDENTIAL_PRESENTATION_VERIFIER_CA_CERTIFICATE_CREATE_FAIL
### Request Body
Verifier root CA certificate payload
### Responses
#### 200 - Verifier root CA certificate created
```json
{
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"certificateFingerprint": "a3b2c1d4e5f60718293a4b5c6d7e8f90123456789abcdef0123456789abcdef0",
"certificateData": {
"commonName": "Example Verifier",
"country": "US",
"notAfter": "2024-10-22T00:00:00Z",
"notBefore": "2023-10-22T00:00:00Z"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 409 - Maximum number of verifier root certificates reached. Please delete an existing certificate before creating a new one.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a verifier root CA certificate
## Endpoint
```
GET /v2/presentations/certificates/ca/{certificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/certificates/ca/{certificateId}`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Retrieves an existing verifier root CA certificate.
### **Analytic events**
* CREDENTIAL_PRESENTATION_VERIFIER_CA_CERTIFICATE_RETRIEVE_START
* CREDENTIAL_PRESENTATION_VERIFIER_CA_CERTIFICATE_RETRIEVE_SUCCESS
* CREDENTIAL_PRESENTATION_VERIFIER_CA_CERTIFICATE_RETRIEVE_FAIL
### Responses
#### 200 - Verifier root CA certificate retrieved
```json
{
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"certificateFingerprint": "a3b2c1d4e5f60718293a4b5c6d7e8f90123456789abcdef0123456789abcdef0",
"certificateData": {
"commonName": "Example Verifier",
"country": "US",
"notAfter": "2024-10-22T00:00:00Z",
"notBefore": "2023-10-22T00:00:00Z"
}
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a verifier root CA certificate
## Endpoint
```
DELETE /v2/presentations/certificates/ca/{certificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/certificates/ca/{certificateId}`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Deletes an existing verifier root CA certificate.
### **Analytic events**
* PRESENTATION_VERIFIER_CA_CERTIFICATE_DELETE_START
* PRESENTATION_VERIFIER_CA_CERTIFICATE_DELETE_SUCCESS
* PRESENTATION_VERIFIER_CA_CERTIFICATE_DELETE_FAIL
### Responses
#### 204 - Verifier root CA certificate deleted
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a verifier root CA certificate
## Endpoint
```
PUT /v2/presentations/certificates/ca/{certificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/certificates/ca/{certificateId}`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Updates an existing verifier root CA certificate.
### **Analytic events**
* CREDENTIAL_PRESENTATION_VERIFIER_CA_CERTIFICATE_UPDATE_START
* CREDENTIAL_PRESENTATION_VERIFIER_CA_CERTIFICATE_UPDATE_SUCCESS
* CREDENTIAL_PRESENTATION_VERIFIER_CA_CERTIFICATE_UPDATE_FAIL
### Request Body
Verifier root CA certificate payload
### Responses
#### 200 - Verifier root CA certificate updated
```json
{
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"certificateFingerprint": "a3b2c1d4e5f60718293a4b5c6d7e8f90123456789abcdef0123456789abcdef0",
"certificateData": {
"commonName": "Example Verifier",
"country": "US",
"notAfter": "2024-10-22T00:00:00Z",
"notBefore": "2023-10-22T00:00:00Z"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all Verification request signers
## Endpoint
```
GET /v2/presentations/certificates/verifier-signers
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/certificates/verifier-signers`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Retrieves all Verification request signers.
### **Analytic events**
* CREDENTIAL_PRESENTATION_VERIFIER_SIGNER_CERTIFICATE_RETRIEVE_LIST_START
* CREDENTIAL_PRESENTATION_VERIFIER_SIGNER_CERTIFICATE_RETRIEVE_LIST_SUCCESS
* CREDENTIAL_PRESENTATION_VERIFIER_SIGNER_CERTIFICATE_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Verification request signers retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create a Verification request signer
## Endpoint
```
POST /v2/presentations/certificates/verifier-signers
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/certificates/verifier-signers`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Creates a Verification request signer.
- Only available in implementations using unmanaged (external) Verifier root CA certificates.
- A maximum of five Verification request signers can be created per tenant.
### **Analytic events**
* CREDENTIAL_PRESENTATION_VERIFIER_SIGNER_CERTIFICATE_CREATE_START
* CREDENTIAL_PRESENTATION_VERIFIER_SIGNER_CERTIFICATE_CREATE_SUCCESS
* CREDENTIAL_PRESENTATION_VERIFIER_SIGNER_CERTIFICATE_CREATE_FAIL
### Request Body
### Responses
#### 201 - Verification request signer created
```json
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"csrPem": "-----BEGIN CERTIFICATE REQUEST-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE REQUEST-----",
"caId": "b0aae560-10e7-4247-8e96-7cdd3578a1e2",
"active": false
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a Verification request signer
## Endpoint
```
GET /v2/presentations/certificates/verifier-signers/{verifierSignerId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/certificates/verifier-signers/{verifierSignerId}`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Retrieves a Verification request signer.
### **Analytic events**
* CREDENTIAL_PRESENTATION_VERIFIER_SIGNER_CERTIFICATE_RETRIEVE_START
* CREDENTIAL_PRESENTATION_VERIFIER_SIGNER_CERTIFICATE_RETRIEVE_SUCCESS
* CREDENTIAL_PRESENTATION_VERIFIER_SIGNER_CERTIFICATE_RETRIEVE_FAIL
### Responses
#### 200 - Verification request signer retrieved
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a Verification request signer
## Endpoint
```
DELETE /v2/presentations/certificates/verifier-signers/{verifierSignerId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/certificates/verifier-signers/{verifierSignerId}`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Deletes a Verification request signer.
Only available in implementations using unmanaged (external) Verifier root CA certificates.
### **Analytic events**
* CREDENTIAL_PRESENTATION_VERIFIER_SIGNER_CERTIFICATE_DELETE_START
* CREDENTIAL_PRESENTATION_VERIFIER_SIGNER_CERTIFICATE_DELETE_SUCCESS
* CREDENTIAL_PRESENTATION_VERIFIER_SIGNER_CERTIFICATE_DELETE_FAIL
### Responses
#### 204 - Verification request signer deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a Verification request signer
## Endpoint
```
PUT /v2/presentations/certificates/verifier-signers/{verifierSignerId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/certificates/verifier-signers/{verifierSignerId}`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Updates a Verification request signer by:
- Providing a Verification Request Signer Certificate (VRSC) in PEM format that matches its Certificate Signing Request (CSR).
- Activating or deactivating the VRSC signer. Only VRSC signers with a valid PEM certificate can be activated.
- The `certificatePem` field becomes immutable after it's updated for the first time.
Only available in implementations using unmanaged (external) Verifier root CA certificates.
### **Analytic events**
* CREDENTIAL_PRESENTATION_VERIFIER_SIGNER_CERTIFICATE_UPDATE_START
* CREDENTIAL_PRESENTATION_VERIFIER_SIGNER_CERTIFICATE_UPDATE_SUCCESS
* CREDENTIAL_PRESENTATION_VERIFIER_SIGNER_CERTIFICATE_UPDATE_FAIL
### Request Body
```json
{
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----"
}
```
### Responses
#### 200 - Verification request signer updated
```json
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"certificateFingerprint": "f6cad6e579d70b3973efa60624af731a580d1a11a7579e70f2f10f059dc86172",
"certificateData": {
"commonName": "example.com",
"country": "US",
"notAfter": "2024-10-22T00:00:00Z",
"notBefore": "2023-10-22T00:00:00Z"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all verifier applications
## Endpoint
```
GET /v2/presentations/applications
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/applications`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Retrieves all configured mDocs online verifier applications.
### **Analytic events**
* CREDENTIAL_PRESENTATION_VERIFIER_APPLICATION_RETRIEVE_LIST_START
* CREDENTIAL_PRESENTATION_VERIFIER_APPLICATION_RETRIEVE_LIST_SUCCESS
* CREDENTIAL_PRESENTATION_VERIFIER_APPLICATION_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Verifier applications retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM",
"data": [
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c",
"name": "Example Verifier Web Application"
}
]
}
```
# Create verifier application
## Endpoint
```
POST /v2/presentations/applications
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/applications`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Creates an mDocs online verifier application.
Once created, application instances can be registered and interact with the MATTR VII platform (for example, to verify credential presentations).
### **Analytic events**
* CREDENTIAL_PRESENTATION_VERIFIER_APPLICATION_CREATE_START
* CREDENTIAL_PRESENTATION_VERIFIER_APPLICATION_CREATE_SUCCESS
* CREDENTIAL_PRESENTATION_VERIFIER_APPLICATION_CREATE_FAIL
### Request Body
Verifier application payload
```json
{
"name": "Example Verifier Web Application"
}
```
### Responses
#### 201 - Verifier application created
```json
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c",
"name": "Example Verifier Web Application"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a verifier application
## Endpoint
```
GET /v2/presentations/applications/{applicationId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/applications/{applicationId}`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Retrieves an existing mDocs online verifier application.
### **Analytic events**
* CREDENTIAL_PRESENTATION_VERIFIER_APPLICATION_RETRIEVE_START
* CREDENTIAL_PRESENTATION_VERIFIER_APPLICATION_RETRIEVE_SUCCESS
* CREDENTIAL_PRESENTATION_VERIFIER_APPLICATION_RETRIEVE_FAIL
### Responses
#### 200 - Verifier application retrieved
```json
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c",
"name": "Example Verifier Web Application"
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a verifier application
## Endpoint
```
DELETE /v2/presentations/applications/{applicationId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/applications/{applicationId}`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Deletes an existing verifier application. Once deleted, any associated application instances will no longer be able to interact with the MATTR VII platform.
### **Analytic events**
* PRESENTATION_VERIFIER_APPLICATION_DELETE_START
* PRESENTATION_VERIFIER_APPLICATION_DELETE_SUCCESS
* PRESENTATION_VERIFIER_APPLICATION_DELETE_FAIL
### Responses
#### 204 - Verifier application deleted
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update verifier application
## Endpoint
```
PUT /v2/presentations/applications/{applicationId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/applications/{applicationId}`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Updates an existing mDocs online verifier application.
### **Analytic events**
* CREDENTIAL_PRESENTATION_VERIFIER_APPLICATION_UPDATE_START
* CREDENTIAL_PRESENTATION_VERIFIER_APPLICATION_UPDATE_SUCCESS
* CREDENTIAL_PRESENTATION_VERIFIER_APPLICATION_UPDATE_FAIL
### Request Body
Verifier application payload
```json
{
"name": "Example Verifier Web Application"
}
```
### Responses
#### 200 - Verifier application updated
```json
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c",
"name": "Example Verifier Web Application"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all registered verifier application instances
## Endpoint
```
GET /v2/presentations/applications/{applicationId}/instances
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/applications/{applicationId}/instances`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Retrieves all registered instances of a verifier application.
This can be used by application owners to view all registered instances for a given verifier application.
### **Analytic events**
* CREDENTIAL_PRESENTATION_VERIFIER_APPLICATION_INSTANCE_RETRIEVE_LIST_START
* CREDENTIAL_PRESENTATION_VERIFIER_APPLICATION_INSTANCE_RETRIEVE_LIST_SUCCESS
* CREDENTIAL_PRESENTATION_VERIFIER_APPLICATION_INSTANCE_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Verifier application instances retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM",
"data": [
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c",
"appAttestationType": "key_attestation",
"registeredAt": "2023-10-05T14:48:00.000Z",
"licenseExpiresAt": "2024-10-05T14:48:00.000Z",
"lastAttestedAt": "2023-12-01T10:30:00.000Z",
"externalReferenceId": "external-ref-12345",
"deviceDetails": {
"deviceModel": "iPhone 12",
"deviceMake": "Apple",
"osVersion": "iOS 14.4"
},
"sdkDetails": {
"sdkVersion": "1.2.3"
}
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a registered verifier application instance
## Endpoint
```
GET /v2/presentations/applications/{applicationId}/instances/{instanceId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/applications/{applicationId}/instances/{instanceId}`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Retrieves a registered instance of a verifier application.
### **Analytic events**
* CREDENTIAL_PRESENTATION_VERIFIER_APPLICATION_INSTANCE_RETRIEVE_START
* CREDENTIAL_PRESENTATION_VERIFIER_APPLICATION_INSTANCE_RETRIEVE_SUCCESS
* CREDENTIAL_PRESENTATION_VERIFIER_APPLICATION_INSTANCE_RETRIEVE_FAIL
### Responses
#### 200 - Verifier application instance retrieved
```json
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c",
"appAttestationType": "key_attestation",
"registeredAt": "2023-10-05T14:48:00.000Z",
"licenseExpiresAt": "2024-10-05T14:48:00.000Z",
"lastAttestedAt": "2023-12-01T10:30:00.000Z",
"externalReferenceId": "external-ref-12345",
"deviceDetails": {
"deviceModel": "iPhone 12",
"deviceMake": "Apple",
"osVersion": "iOS 14.4"
},
"sdkDetails": {
"sdkVersion": "1.2.3"
}
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a registered verifier application instance
## Endpoint
```
DELETE /v2/presentations/applications/{applicationId}/instances/{instanceId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/applications/{applicationId}/instances/{instanceId}`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Deletes a registered instance of a verifier application.
Deleted instances will no longer be able to interact with the platform or receive tokens, and any existing tokens will be revoked.
Application owners can use this endpoint to remove individual instances that are no longer needed or were registered by mistake, without affecting the entire verifier application or its other instances. This is useful for cleaning up test instances or managing specific devices, rather than deleting the whole application and all its associated instances.
### **Analytic events**
* CREDENTIAL_PRESENTATION_VERIFIER_APPLICATION_INSTANCE_DELETE_START
* CREDENTIAL_PRESENTATION_VERIFIER_APPLICATION_INSTANCE_DELETE_SUCCESS
* CREDENTIAL_PRESENTATION_VERIFIER_APPLICATION_INSTANCE_DELETE_FAIL
### Responses
#### 204 - Verifier application instance deleted
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all wallet providers
## Endpoint
```
GET /v2/presentations/wallet-providers
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/wallet-providers`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Retrieves all existing wallet providers that can present mDocs for online verification.
### **Analytic events**
* CREDENTIAL_PRESENTATION_WALLET_PROVIDER_RETRIEVE_LIST_START
* CREDENTIAL_PRESENTATION_WALLET_PROVIDER_RETRIEVE_LIST_SUCCESS
* CREDENTIAL_PRESENTATION_WALLET_PROVIDER_RETRIEVE_LIST_FAIL
### Responses
#### 200 - Wallet providers retrieved
```json
[
{
"id": "e63a2e46-5afa-48f9-bcc2-2114cf5f331b",
"name": "Example wallet provider",
"openid4vpConfiguration": {
"authorizationEndpoint": "com-example.wallet://"
}
}
]
```
# Create wallet provider
## Endpoint
```
POST /v2/presentations/wallet-providers
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/wallet-providers`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Creates a wallet provider that can present mDocs for online verification.
### **Analytic events**
* CREDENTIAL_PRESENTATION_WALLET_PROVIDER_CREATE_START
* CREDENTIAL_PRESENTATION_WALLET_PROVIDER_CREATE_SUCCESS
* CREDENTIAL_PRESENTATION_WALLET_PROVIDER_CREATE_FAIL
### Request Body
Wallet provider payload
```json
{
"name": "Example wallet provider",
"openid4vpConfiguration": {
"authorizationEndpoint": "com-example.wallet://"
}
}
```
### Responses
#### 200 - Wallet provider created
```json
{
"id": "e63a2e46-5afa-48f9-bcc2-2114cf5f331b",
"name": "Example wallet provider",
"openid4vpConfiguration": {
"authorizationEndpoint": "com-example.wallet://"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a wallet provider
## Endpoint
```
GET /v2/presentations/wallet-providers/{walletProviderId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/wallet-providers/{walletProviderId}`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Retrieves an existing wallet provider that can present mDocs for online verification.
### **Analytic events**
* CREDENTIAL_PRESENTATION_WALLET_PROVIDER_RETRIEVE_START
* CREDENTIAL_PRESENTATION_WALLET_PROVIDER_RETRIEVE_SUCCESS
* CREDENTIAL_PRESENTATION_WALLET_PROVIDER_RETRIEVE_FAIL
### Responses
#### 200 - Wallet provider retrieved
```json
{
"id": "e63a2e46-5afa-48f9-bcc2-2114cf5f331b",
"name": "Example wallet provider",
"openid4vpConfiguration": {
"authorizationEndpoint": "com-example.wallet://"
}
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a wallet provider
## Endpoint
```
DELETE /v2/presentations/wallet-providers/{walletProviderId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/wallet-providers/{walletProviderId}`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Deletes an existing wallet provider that can present mDocs for online verification.
### **Analytic events**
* PRESENTATION_WALLET_PROVIDER_DELETE_START
* PRESENTATION_WALLET_PROVIDER_DELETE_SUCCESS
* PRESENTATION_WALLET_PROVIDER_DELETE_FAIL
### Responses
#### 204 - Wallet provider deleted
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a wallet provider
## Endpoint
```
PUT /v2/presentations/wallet-providers/{walletProviderId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/wallet-providers/{walletProviderId}`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Updates an existing wallet provider that can present mDocs for online verification.
### **Analytic events**
* CREDENTIAL_PRESENTATION_WALLET_PROVIDER_UPDATE_START
* CREDENTIAL_PRESENTATION_WALLET_PROVIDER_UPDATE_SUCCESS
* CREDENTIAL_PRESENTATION_WALLET_PROVIDER_UPDATE_FAIL
### Request Body
Wallet provider payload
```json
{
"name": "Example wallet provider",
"openid4vpConfiguration": {
"authorizationEndpoint": "com-example.wallet://"
}
}
```
### Responses
#### 200 - Wallet provider updated
```json
{
"id": "e63a2e46-5afa-48f9-bcc2-2114cf5f331b",
"name": "Example wallet provider",
"openid4vpConfiguration": {
"authorizationEndpoint": "com-example.wallet://"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve presentation session result
## Endpoint
```
GET /v2/presentations/sessions/{sessionId}/result
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/sessions/{sessionId}/result`
### Authorization
Bearer token required.
## Description
Retrieves the result of an online presentation session by providing the session's ID.
When a holder responds to a verification request, the result indicates whether the presentation succeeded and whether the credentials were verified. The response can be a `PresentationSuccessResult` (holder completed the presentation workflow) or a `PresentationFailureResult` (holder was unable to complete the workflow).
A successful presentation may include verified credentials, credential errors (requested credentials not provided), claim errors (specific claims that failed verification), or a combination of these outcomes.
Your backend should validate that the returned `challenge` matches the challenge generated before starting the session to protect against session replay attacks.
For detailed information about result structures, error types, and complete examples, see the [handling verification results guide](https://learn.mattr.global/docs/verification/remote-web-verifiers/guides/handling-verification-results).
### **Analytic events**
* CREDENTIAL_PRESENTATION_SESSION_RESULT_RETRIEVE_START
* CREDENTIAL_PRESENTATION_SESSION_RESULT_RETRIEVE_SUCCESS
* CREDENTIAL_PRESENTATION_SESSION_RESULT_RETRIEVE_FAIL
### Responses
#### 200 - Session result retrieved
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all Apple Identity Access CSRs
## Endpoint
```
GET /v2/presentations/certificates/apple-identity-access-certificates
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/certificates/apple-identity-access-certificates`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Retrieves all Apple Identity Access CSRs created by the tenant.
### **Analytic events**
* CREDENTIAL_PRESENTATION_APPLE_IDENTITY_ACCESS_CERTIFICATE_RETRIEVE_LIST_START
* CREDENTIAL_PRESENTATION_APPLE_IDENTITY_ACCESS_CERTIFICATE_RETRIEVE_LIST_SUCCESS
* CREDENTIAL_PRESENTATION_APPLE_IDENTITY_ACCESS_CERTIFICATE_RETRIEVE_LIST_FAIL
### Responses
#### 200 - Apple Identity Access CSRs retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM",
"data": [
{
"id": "fd44e792-45ac-11f0-bef8-bb24f133065e",
"teamId": "A2B3C4D5E6",
"merchantId": "com.domain.subdomain"
}
]
}
```
# Create an Apple Identity Access CSR
## Endpoint
```
POST /v2/presentations/certificates/apple-identity-access-certificates
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/certificates/apple-identity-access-certificates`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Creates an Apple Identity Access Certificate Signing Request (CSR) that can be uploaded to the Apple Developer Portal.
This certificate contains the public key that will be used to decrypt the response from the Apple Wallet.
### **Analytic events**
* CREDENTIAL_PRESENTATION_APPLE_IDENTITY_ACCESS_CERTIFICATE_CREATE_START
* CREDENTIAL_PRESENTATION_APPLE_IDENTITY_ACCESS_CERTIFICATE_CREATE_SUCCESS
* CREDENTIAL_PRESENTATION_APPLE_IDENTITY_ACCESS_CERTIFICATE_CREATE_FAIL
### Request Body
Apple Identity Access CSR payload
```json
{
"teamId": "A2B3C4D5E6",
"merchantId": "com.domain.subdomain"
}
```
### Responses
#### 201 - Apple Identity Access CSR created
```json
{
"id": "fd44e792-45ac-11f0-bef8-bb24f133065e",
"teamId": "A2B3C4D5E6",
"merchantId": "com.domain.subdomain"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve an Apple Identity Access CSR
## Endpoint
```
GET /v2/presentations/certificates/apple-identity-access-certificates/{certificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/certificates/apple-identity-access-certificates/{certificateId}`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Retrieves an existing Apple Identity Access CSR.
### **Analytic events**
* CREDENTIAL_PRESENTATION_APPLE_IDENTITY_ACCESS_CERTIFICATE_RETRIEVE_START
* CREDENTIAL_PRESENTATION_APPLE_IDENTITY_ACCESS_CERTIFICATE_RETRIEVE_SUCCESS
* CREDENTIAL_PRESENTATION_APPLE_IDENTITY_ACCESS_CERTIFICATE_RETRIEVE_FAIL
### Responses
#### 200 - Apple Identity Access Certificate CSR retrieved
```json
{
"id": "fd44e792-45ac-11f0-bef8-bb24f133065e",
"teamId": "A2B3C4D5E6",
"merchantId": "com.domain.subdomain"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete an Apple Identity Access CSR
## Endpoint
```
DELETE /v2/presentations/certificates/apple-identity-access-certificates/{certificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v2/presentations/certificates/apple-identity-access-certificates/{certificateId}`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Deletes an existing Apple Identity Access CSR.
### **Analytic events**
* CREDENTIAL_PRESENTATION_APPLE_IDENTITY_ACCESS_CERTIFICATE_DELETE_START
* CREDENTIAL_PRESENTATION_APPLE_IDENTITY_ACCESS_CERTIFICATE_DELETE_SUCCESS
* CREDENTIAL_PRESENTATION_APPLE_IDENTITY_ACCESS_CERTIFICATE_DELETE_FAIL
### Responses
#### 204 - Apple Identity Access Certificate CSR deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Sign a JSON credential
## Endpoint
```
POST /v2/credentials/web-semantic/sign
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/web-semantic/sign`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns a signed JSON credential generated from a provided valid payload.
### **Analytic events**
* CREDENTIAL_WEB_SEMANTIC_SIGN_START
* CREDENTIAL_WEB_SEMANTIC_SIGN_SUCCESS
* CREDENTIAL_WEB_SEMANTIC_SIGN_FAIL
### Request Body
JSON credential payload to sign
```json
{
"payload": {
"name": "Course credential",
"description": "This credential shows that the person has attended the mention course and attained the relevant awards.",
"@context": [
"https://optionalschema.example/"
],
"type": [
"EducationalOccupationalCredential",
"AlumniCredential"
],
"credentialSubject": {
"id": "did:example:abcdb1f712ebc6f1c276e12ec21",
"givenName": "Jamie",
"familyName": "Doe",
"alumniOf": "Example University"
},
"credentialBranding": {
"backgroundColor": "#B00AA0",
"watermarkImageUrl": "https://example.edu/img/watermark.png"
},
"issuer": {
"id": "did:issuer:abcdb1f712ebc6f1c276e12ec21",
"name": "ABC University",
"logoUrl": "https://example.edu/img/logo.png",
"iconUrl": "https://example.edu/img/icon.png"
},
"expirationDate": "2024-02-01T08:12:38.156Z",
"issuanceDate": "2023-02-01T08:12:38.156Z"
},
"proofType": "Ed25519Signature2018",
"tag": "identifier123"
}
```
### Responses
#### 200 - JSON Credential signed
```json
{
"id": "873277c0-a162-11ea-8a1d-a111119347e6",
"credential": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://mattr.global/contexts/vc-extensions/v2",
"https://w3id.org/vc-revocation-list-2020/v1",
"https://optionalschema.example/"
],
"type": [
"VerifiableCredential",
"AlumniCredential"
],
"issuanceDate": "2020-05-02T12:06:29.156Z",
"credentialStatus": {
"id": "https://tenant.vii.mattr.global/v1/revocation-lists/cc641396-3750-43c8-b8b8-f30d74eb3fb3#1",
"type": "RevocationList2020Status",
"revocationListIndex": 1,
"revocationListCredential": "https://tenant.vii.mattr.global/v1/revocation-lists/cc641396-3750-43c8-b8b8-f30d74eb3fb3"
},
"credentialSubject": {
"givenName": "Jamie",
"familyName": "Doe",
"alumniOf": "Example University"
},
"proof": {
"type": "Ed25519Signature2018",
"created": "2020-05-02T12:06:29Z",
"jws": "EXAMPLE_JWS_TOKEN_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
"proofPurpose": "assertionMethod",
"verificationMethod": "did:web:organization.com"
},
"name": "Alumni Credential",
"description": "This credential shows that the person has attended the mentioned university."
},
"tag": "identifier123",
"credentialStatus": {
"id": "https://tenant.vii.mattr.global/v1/revocation-lists/cc641396-3750-43c8-b8b8-f30d74eb3fb3#1",
"type": "RevocationList2020Status",
"revocationListIndex": 1,
"revocationListCredential": "https://tenant.vii.mattr.global/v1/revocation-lists/cc641396-3750-43c8-b8b8-f30d74eb3fb3"
},
"issuanceDate": "2020-05-02T12:06:29.156Z"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all credential data
## Endpoint
```
GET /v2/credentials/web-semantic
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/web-semantic`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns all available data for existing credentials:
- For credentials that were created with the `persist` flag set to `true`, the response contains both the credential and its metadata.
- For credentials that were created with the persist flag set to `false`, the response only contains the metadata (`id`, `tag`, `credentialStatus`, `issuanceDate`).
### **Analytic events**
* CREDENTIAL_WEB_SEMANTIC_RETRIEVE_LIST_START
* CREDENTIAL_WEB_SEMANTIC_RETRIEVE_LIST_SUCCESS
* CREDENTIAL_WEB_SEMANTIC_RETRIEVE_LIST_FAIL
### Query Parameters
- `tag`: string
Optional tag to filter on.
- `type`: string
Optional credential type to filter on.
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Credentials data retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM",
"data": [
{
"id": "873277c0-a162-11ea-8a1d-a111119347e6",
"credential": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"type": [
"VerifiableCredential",
"AlumniCredential"
],
"credentialBranding": {
"backgroundColor": "#B00AA0",
"watermarkImageUrl": "https://example.edu/img/watermark.png"
},
"issuanceDate": "2020-05-02T12:06:29.156Z",
"credentialStatus": {
"id": "https://tenant.vii.mattr.global/v1/revocation-lists/cc641396-3750-43c8-b8b8-f30d74eb3fb3#1",
"type": "RevocationList2020Status",
"revocationListIndex": 1,
"revocationListCredential": "https://tenant.vii.mattr.global/v1/revocation-lists/cc641396-3750-43c8-b8b8-f30d74eb3fb3"
},
"credentialSubject": {
"givenName": "Jamie",
"familyName": "Doe",
"alumniOf": "Example University"
},
"proof": {
"type": "Ed25519Signature2018",
"created": "2020-05-02T12:06:29Z",
"jws": "EXAMPLE_JWS_TOKEN_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
"proofPurpose": "assertionMethod",
"verificationMethod": "did:web:organization.com"
},
"name": "Alumni Credential",
"description": "This credential shows that the person has attended the mentioned university."
},
"tag": "identifier123",
"credentialStatus": {
"id": "https://tenant.vii.mattr.global/v1/revocation-lists/cc641396-3750-43c8-b8b8-f30d74eb3fb3#1",
"type": "RevocationList2020Status",
"revocationListIndex": 1,
"revocationListCredential": "https://tenant.vii.mattr.global/v1/revocation-lists/cc641396-3750-43c8-b8b8-f30d74eb3fb3"
},
"issuanceDate": "2020-05-02T12:06:29.156Z"
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve credential data
## Endpoint
```
GET /v2/credentials/web-semantic/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/web-semantic/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns all available data for an existing credential that matches the provided ID:
- For credentials that were created with the `persist` flag set to `true`, the response contains both the credential and its metadata.
- For credentials that were created with the persist flag set to `false`, the response only contains the metadata (`id`, `tag`, `credentialStatus`, `issuanceDate`)
### **Analytic events**
* CREDENTIAL_WEB_SEMANTIC_RETRIEVE_START
* CREDENTIAL_WEB_SEMANTIC_RETRIEVE_SUCCESS
* CREDENTIAL_WEB_SEMANTIC_RETRIEVE_FAIL
### Responses
#### 200 - Credential data retrieved
```json
{
"id": "873277c0-a162-11ea-8a1d-a111119347e6",
"credential": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"type": [
"VerifiableCredential",
"AlumniCredential"
],
"credentialBranding": {
"backgroundColor": "#B00AA0",
"watermarkImageUrl": "https://example.edu/img/watermark.png"
},
"issuanceDate": "2020-05-02T12:06:29.156Z",
"credentialStatus": {
"id": "https://tenant.vii.mattr.global/v1/revocation-lists/cc641396-3750-43c8-b8b8-f30d74eb3fb3#1",
"type": "RevocationList2020Status",
"revocationListIndex": 1,
"revocationListCredential": "https://tenant.vii.mattr.global/v1/revocation-lists/cc641396-3750-43c8-b8b8-f30d74eb3fb3"
},
"credentialSubject": {
"givenName": "Jamie",
"familyName": "Doe",
"alumniOf": "Example University"
},
"proof": {
"type": "Ed25519Signature2018",
"created": "2020-05-02T12:06:29Z",
"jws": "EXAMPLE_JWS_TOKEN_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
"proofPurpose": "assertionMethod",
"verificationMethod": "did:web:organization.com"
},
"name": "Alumni Credential",
"description": "This credential shows that the person has attended the mentioned university."
},
"tag": "identifier123",
"credentialStatus": {
"id": "https://tenant.vii.mattr.global/v1/revocation-lists/cc641396-3750-43c8-b8b8-f30d74eb3fb3#1",
"type": "RevocationList2020Status",
"revocationListIndex": 1,
"revocationListCredential": "https://tenant.vii.mattr.global/v1/revocation-lists/cc641396-3750-43c8-b8b8-f30d74eb3fb3"
},
"issuanceDate": "2020-05-02T12:06:29.156Z"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete credential data
## Endpoint
```
DELETE /v2/credentials/web-semantic/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/web-semantic/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Deletes all stored data for an existing credential that matches the provided ID. If the credential is revocable, it will also be permanently revoked.
Removed credential data cannot be recovered.
### **Analytic events**
* CREDENTIAL_WEB_SEMANTIC_DELETE_START
* CREDENTIAL_WEB_SEMANTIC_DELETE_SUCCESS
* CREDENTIAL_WEB_SEMANTIC_DELETE_FAIL
### Responses
#### 204 - Credential deleted and revoked if revocable
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve credential revocation status
## Endpoint
```
GET /v2/credentials/web-semantic/{id}/revocation-status
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/web-semantic/{id}/revocation-status`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns the revocation status of the credential matching the provided ID.
### **Analytic events**
* CREDENTIAL_WEB_SEMANTIC_REVOCATION_RETRIEVE_START
* CREDENTIAL_WEB_SEMANTIC_REVOCATION_RETRIEVE_SUCCESS
* CREDENTIAL_WEB_SEMANTIC_REVOCATION_RETRIEVE_FAIL
### Responses
#### 200 - Credential status
```json
{
"isRevoked": false
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Set credential revocation status
## Endpoint
```
POST /v2/credentials/web-semantic/{id}/revocation-status
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/web-semantic/{id}/revocation-status`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Sets the revocation status of the credential that matches the provided ID as `true` (revoked) or `false` (unrevoked).
### **Analytic events**
* CREDENTIAL_WEB_SEMANTIC_REVOCATION_SET_STATUS_START
* CREDENTIAL_WEB_SEMANTIC_REVOCATION_SET_STATUS_SUCCESS
* CREDENTIAL_WEB_SEMANTIC_REVOCATION_SET_STATUS_FAIL
### Request Body
Setting the revocation status
```json
{
"isRevoked": false
}
```
### Responses
#### 200 - Revocation status updated
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve revocation list
## Endpoint
```
GET /v2/credentials/web-semantic/revocation-lists/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/web-semantic/revocation-lists/{id}`
### Authorization
None required.
## Description
Retrieves a JSON credential revocation list by providing its ID.
This endpoint is intended for public consumption, and as such does not require authentication.
### Responses
#### 200 - Revocation list retrieved
```json
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"type": [
"VerifiableCredential",
"AlumniCredential"
],
"credentialBranding": {
"backgroundColor": "#B00AA0",
"watermarkImageUrl": "https://example.edu/img/watermark.png"
},
"issuanceDate": "2020-05-02T12:06:29.156Z",
"credentialStatus": {
"id": "https://tenant.vii.mattr.global/v1/revocation-lists/cc641396-3750-43c8-b8b8-f30d74eb3fb3#1",
"type": "RevocationList2020Status",
"revocationListIndex": 1,
"revocationListCredential": "https://tenant.vii.mattr.global/v1/revocation-lists/cc641396-3750-43c8-b8b8-f30d74eb3fb3"
},
"credentialSubject": {
"givenName": "Jamie",
"familyName": "Doe",
"alumniOf": "Example University"
},
"proof": {
"type": "Ed25519Signature2018",
"created": "2020-05-02T12:06:29Z",
"jws": "EXAMPLE_JWS_TOKEN_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
"proofPurpose": "assertionMethod",
"verificationMethod": "did:web:organization.com"
},
"name": "Alumni Credential",
"description": "This credential shows that the person has attended the mentioned university."
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create a revocation message payload
## Endpoint
```
POST /v2/credentials/web-semantic/{id}/revocation-status/notification
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/web-semantic/{id}/revocation-status/notification`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns a message in JWM format that can be used to notify subjects based on their credential revocation status change.
To send a notification to the Subject DID holder, use the returned payload with the [encrypt](#operation/encryptMessage) and [send](#operation/sendMessage) endpoints.
### **Analytic events**
* CREDENTIAL_WEB_SEMANTIC_REVOCATION_MESSAGE_PAYLOAD_CREATE_START
* CREDENTIAL_WEB_SEMANTIC_REVOCATION_MESSAGE_PAYLOAD_CREATE_SUCCESS
* CREDENTIAL_WEB_SEMANTIC_REVOCATION_MESSAGE_PAYLOAD_CREATE_FAIL
### Request Body
Create a JWM message payload
```json
{
"from": "did:web:organization.com",
"to": [
"did:key:subjectDid1",
"did:key:subjectDid2",
"did:key:subjectDid3"
]
}
```
### Responses
#### 201 - Revocation message payload created
```json
{
"to": [
"did:key:subjectDid1",
"did:key:subjectDid2",
"did:key:subjectDid3"
],
"from": "did:web:organization.com"
}
```
# Verify a JSON credential
## Endpoint
```
POST /v2/credentials/web-semantic/verify
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/web-semantic/verify`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Verify a JSON credential by providing its payload. The credential is verified against the following criteria:
- Issuer DID can be resolved, so that the referenced DID Document is available and valid and the public key is obtainable.
- Proof is valid and the credential has not been tampered with.
- JSON-LD context is valid for subject claims.
Optional verification checks:
- If `assertExpiry` is set to `true` and the credential has a set expiration date, verification will fail if the expiration date has passed.
- If `checkRevocation` is set to `true` and the provided credential contains a revocation status list, verification will fail if the credential has been set to `revoked`.
### **Analytic events**
* CREDENTIAL_WEB_SEMANTIC_VERIFY_START
* CREDENTIAL_WEB_SEMANTIC_VERIFY_SUCCESS
* CREDENTIAL_WEB_SEMANTIC_VERIFY_FAIL
### Request Body
```json
{
"payload": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://mattr.global/contexts/vc-extensions/v2",
"https://w3id.org/vc-revocation-list-2020/v1",
"https://optionalschema.example/"
],
"type": [
"VerifiableCredential",
"AlumniCredential"
],
"issuanceDate": "2020-05-02T12:06:29.156Z",
"credentialStatus": {
"id": "https://tenant.vii.mattr.global/v1/revocation-lists/cc641396-3750-43c8-b8b8-f30d74eb3fb3#1",
"type": "RevocationList2020Status",
"revocationListIndex": 1,
"revocationListCredential": "https://tenant.vii.mattr.global/v1/revocation-lists/cc641396-3750-43c8-b8b8-f30d74eb3fb3"
},
"credentialSubject": {
"givenName": "Jamie",
"familyName": "Doe",
"alumniOf": "Example University"
},
"proof": {
"type": "Ed25519Signature2018",
"created": "2020-05-02T12:06:29Z",
"jws": "EXAMPLE_JWS_TOKEN_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
"proofPurpose": "assertionMethod",
"verificationMethod": "did:web:organization.com"
},
"name": "Alumni Credential",
"description": "This credential shows that the person has attended the mentioned university."
}
}
```
### Responses
#### 200 - Verification completed
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all presentation templates
## Endpoint
```
GET /v2/credentials/web-semantic/presentations/templates
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/web-semantic/presentations/templates`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Returns a list of all presentation templates on your tenant.
### **Analytic events**
* PRESENTATION_WEB_SEMANTIC_TEMPLATE_RETRIEVE_LIST_START
* PRESENTATION_WEB_SEMANTIC_TEMPLATE_RETRIEVE_LIST_SUCCESS
* PRESENTATION_WEB_SEMANTIC_TEMPLATE_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Presentation templates retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM",
"data": [
{
"id": "64e45290-9980-11ea-b872-f1bee5fb328f",
"domain": "tenant.vii.mattr.global",
"name": "alumni_credential_request"
}
]
}
```
# Create a presentation template
## Endpoint
```
POST /v2/credentials/web-semantic/presentations/templates
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/web-semantic/presentations/templates`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Creates a presentation template defining what type of credential is required for a particular verification workflow. Presentation templates are used to create presentation requests that are shared with a specific holder.
### **Analytic events**
* PRESENTATION_WEB_SEMANTIC_TEMPLATE_CREATE_START
* PRESENTATION_WEB_SEMANTIC_TEMPLATE_CREATE_SUCCESS
* PRESENTATION_WEB_SEMANTIC_TEMPLATE_CREATE_FAIL
### Request Body
The template
### Responses
#### 201 - Presentation template created
```json
{
"id": "64e45290-9980-11ea-b872-f1bee5fb328f",
"domain": "tenant.vii.mattr.global",
"name": "alumni_credential_request"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a presentation template
## Endpoint
```
GET /v2/credentials/web-semantic/presentations/templates/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/web-semantic/presentations/templates/{id}`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Retrieve an existing presentation template by its ID.
### **Analytic events**
* PRESENTATION_WEB_SEMANTIC_TEMPLATE_RETRIEVE_START
* PRESENTATION_WEB_SEMANTIC_TEMPLATE_RETRIEVE_SUCCESS
* PRESENTATION_WEB_SEMANTIC_TEMPLATE_RETRIEVE_FAIL
### Responses
#### 200 - Presentation template retrieved
```json
{
"id": "64e45290-9980-11ea-b872-f1bee5fb328f",
"domain": "tenant.vii.mattr.global",
"name": "alumni_credential_request"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a presentation template
## Endpoint
```
DELETE /v2/credentials/web-semantic/presentations/templates/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/web-semantic/presentations/templates/{id}`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Deletes an existing presentation template by its ID.
### **Analytic events**
* PRESENTATION_WEB_SEMANTIC_TEMPLATE_DELETE_START
* PRESENTATION_WEB_SEMANTIC_TEMPLATE_DELETE_SUCCESS
* PRESENTATION_WEB_SEMANTIC_TEMPLATE_DELETE_FAIL
### Responses
#### 204 - Presentation template deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a presentation template
## Endpoint
```
PUT /v2/credentials/web-semantic/presentations/templates/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/web-semantic/presentations/templates/{id}`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Updates an existing presentation template by its ID.
### **Analytic events**
* PRESENTATION_WEB_SEMANTIC_TEMPLATE_UPDATE_START
* PRESENTATION_WEB_SEMANTIC_TEMPLATE_UPDATE_SUCCESS
* PRESENTATION_WEB_SEMANTIC_TEMPLATE_UPDATE_FAIL
### Request Body
### Responses
#### 200 - OK
```json
{
"id": "64e45290-9980-11ea-b872-f1bee5fb328f",
"domain": "tenant.vii.mattr.global",
"name": "alumni_credential_request"
}
```
# Create a presentation request
## Endpoint
```
POST /v2/credentials/web-semantic/presentations/requests
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/web-semantic/presentations/requests`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Creates a short lived presentation request based on an existing presentation template. The request is returned in the form of a JWM message and must be [signed](#operation/signMessage) and sent to the holder via one of the following methods:
- QR code.
- Deeplink.
- [Encrypted](#operation/encryptMessage) and [sent](#operation/sendMessage) as a wallet notification.
### **Analytic events**
* PRESENTATION_WEB_SEMANTIC_REQUEST_CREATE_START
* PRESENTATION_WEB_SEMANTIC_REQUEST_CREATE_SUCCESS
* PRESENTATION_WEB_SEMANTIC_REQUEST_CREATE_FAIL
### Request Body
The presentation request payload
```json
{
"challenge": "64e45290-9980-11ea-b872-f1bee5fb328f",
"did": "did:key:z6Mkt7bFYc4V2HdAxwhMtaY6cgJckYXwhYdPLJCcnVqzrkpr",
"templateId": "64e45290-9980-11ea-b872-f1bee5fb328f",
"expiresTime": 1592955632103,
"callbackUrl": "https://your-website.com/api/callback"
}
```
### Responses
#### 201 - Presentation request created
```json
{
"id": "c74128a0-9949-11ea-9554-b5a630b3c119",
"callbackUrl": "https://your-website.com/api/callback",
"request": {
"id": "c74128a0-9949-11ea-9554-b5a630b3c119",
"type": "https://mattr.global/schemas/verifiable-presentation/request/QueryByExample",
"from": "did:key:z6MkrYVmyqSA93o4B1GwERM8kaQDMAUKAFV2TC3weQKeg9Gq",
"created_time": 1606709582907,
"expires_time": 2594859115000,
"reply_url": "https://your-website.com/api/callback",
"reply_to": [
"did:key:z6MkrYVmyqSA93o4B1GwERM8kaQDMAUKAFV2TC3weQKeg9Gq"
],
"body": {
"id": "64e45290-9980-11ea-b872-f1bee5fb328f",
"domain": "tenant.vii.mattr.global",
"name": "alumni_credential_request",
"challenge": "e1b35ae0-9e0e-11ea-9bbf-a387b27c9e60"
}
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Verify a verifiable presentation
## Endpoint
```
POST /v2/credentials/web-semantic/presentations/verify
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/web-semantic/presentations/verify`
### Authorization
Bearer token required.
Required roles: admin, verifier
## Description
Verifies a provided verifiable presentation that adheres to the [W3C Verifiable Credential Data Model](https://www.w3.org/TR/vc-data-model/#presentations):
- Ensures the presentation conforms to the VC Data model.
- For each `verifiableCredential` objects:
- Issuer DID can be resolved.
- JSON-LD context is valid for subject claims.
- Proof is valid & the credential has not been tampered with.
- Is not in a `revoked` status on a `RevocationList2020`.
- The proof is valid for each subjectDID to prove ownership.
- Valid proof exists for the presentation `holderDID`.
The request must include a `presentation` object that adheres to the [W3C Verifiable Credential Data Model](https://www.w3.org/TR/vc-data-model/#presentations).
If a `challenge` and/or `domain` is provided they are used for credential verification. Otherwise, the `challenge` and/or `domain` within the presentation proof is used instead.
### **Analytic events**
* PRESENTATION_WEB_SEMANTIC_VERIFY_START
* PRESENTATION_WEB_SEMANTIC_VERIFY_SUCCESS
* PRESENTATION_WEB_SEMANTIC_VERIFY_FAIL
### Request Body
Presentation to verify
```json
{
"presentation": {
"verifiableCredential": [
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"type": [
"VerifiableCredential",
"AlumniCredential"
],
"credentialBranding": {
"backgroundColor": "#B00AA0",
"watermarkImageUrl": "https://example.edu/img/watermark.png"
},
"issuanceDate": "2020-05-02T12:06:29.156Z",
"credentialStatus": {
"id": "https://tenant.vii.mattr.global/v1/revocation-lists/cc641396-3750-43c8-b8b8-f30d74eb3fb3#1",
"type": "RevocationList2020Status",
"revocationListIndex": 1,
"revocationListCredential": "https://tenant.vii.mattr.global/v1/revocation-lists/cc641396-3750-43c8-b8b8-f30d74eb3fb3"
},
"credentialSubject": {
"givenName": "Jamie",
"familyName": "Doe",
"alumniOf": "Example University"
},
"proof": {
"type": "Ed25519Signature2018",
"created": "2020-05-02T12:06:29Z",
"jws": "EXAMPLE_JWS_TOKEN_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
"proofPurpose": "assertionMethod",
"verificationMethod": "did:web:organization.com"
},
"name": "Alumni Credential",
"description": "This credential shows that the person has attended the mentioned university."
}
]
},
"challenge": "3182bdea-63d9-11ea-b6de-3b7c1404d57f",
"domain": "example.com"
}
```
### Responses
#### 200 - Presentation verification completed
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all JSON credentials configurations
## Endpoint
```
GET /v2/credentials/web-semantic/configurations
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/web-semantic/configurations`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns a list of all JSON credential configurations on your tenant.
### **Analytic events**
* CREDENTIAL_WEB_SEMANTIC_CREDENTIAL_CONFIGURATION_RETRIEVE_LIST_START
* CREDENTIAL_WEB_SEMANTIC_CREDENTIAL_CONFIGURATION_RETRIEVE_LIST_SUCCESS
* CREDENTIAL_WEB_SEMANTIC_CREDENTIAL_CONFIGURATION_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
- `type`: string
The optional credential type to filter on
### Responses
#### 200 - JSON credentials configurations retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM",
"data": [
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"name": "Course credential",
"description": "This credential shows that the person has attended a course.",
"type": "CourseCredential",
"additionalTypes": [
"AlumniCredential",
"EducationCredential"
],
"contexts": [
"https://optionalschema.example/"
],
"issuer": {
"name": "ABC University",
"logoUrl": "https://example.edu/img/logo.png",
"iconUrl": "https://example.edu/img/icon.png"
},
"proofType": "Ed25519Signature2018",
"credentialBranding": {
"backgroundColor": "#B00AA0",
"watermarkImageUrl": "https://example.edu/img/watermark.png"
},
"claimMappings": {
"firstName": {
"mapFrom": "claims.given_name",
"required": true
},
"address": {
"mapFrom": "claims.address.formatted"
},
"picture": {
"mapFrom": "claims.picture",
"defaultValue": "http://example.edu/img/placeholder.png"
},
"badge": {
"defaultValue": "http://example.edu/img/badge.png"
},
"providerSubjectId": {
"mapFrom": "authenticationProvider.subjectId"
}
},
"expiresIn": {
"months": 3
}
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create a JSON credentials configuration
## Endpoint
```
POST /v2/credentials/web-semantic/configurations
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/web-semantic/configurations`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Creates a new JSON credentials configuration, a specific set of rules and parameters that are used to create and validate a particular type of verifiable credential. These rules and parameters define how the credential is structured and what data it contains when issued.
### **Analytic events**
* CREDENTIAL_WEB_SEMANTIC_CREDENTIAL_CONFIGURATION_CREATE_START
* CREDENTIAL_WEB_SEMANTIC_CREDENTIAL_CONFIGURATION_CREATE_SUCCESS
* CREDENTIAL_WEB_SEMANTIC_CREDENTIAL_CONFIGURATION_CREATE_FAIL
### Request Body
The credential configuration payload
```json
{
"name": "Course credential",
"description": "This credential shows that the person has attended a course.",
"type": "CourseCredential",
"additionalTypes": [
"AlumniCredential",
"EducationCredential"
],
"contexts": [
"https://optionalschema.example/"
],
"issuer": {
"name": "ABC University",
"logoUrl": "https://example.edu/img/logo.png",
"iconUrl": "https://example.edu/img/icon.png"
},
"proofType": "Ed25519Signature2018",
"credentialBranding": {
"backgroundColor": "#B00AA0",
"watermarkImageUrl": "https://example.edu/img/watermark.png"
},
"claimMappings": {
"firstName": {
"mapFrom": "claims.given_name",
"required": true
},
"address": {
"mapFrom": "claims.address.formatted"
},
"picture": {
"mapFrom": "claims.picture",
"defaultValue": "http://example.edu/img/placeholder.png"
},
"badge": {
"defaultValue": "http://example.edu/img/badge.png"
},
"providerSubjectId": {
"mapFrom": "authenticationProvider.subjectId"
}
},
"expiresIn": {
"months": 3
}
}
```
### Responses
#### 201 - JSON credentials configuration created
```json
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"name": "Course credential",
"description": "This credential shows that the person has attended a course.",
"type": "CourseCredential",
"additionalTypes": [
"AlumniCredential",
"EducationCredential"
],
"contexts": [
"https://optionalschema.example/"
],
"issuer": {
"name": "ABC University",
"logoUrl": "https://example.edu/img/logo.png",
"iconUrl": "https://example.edu/img/icon.png"
},
"proofType": "Ed25519Signature2018",
"credentialBranding": {
"backgroundColor": "#B00AA0",
"watermarkImageUrl": "https://example.edu/img/watermark.png"
},
"claimMappings": {
"firstName": {
"mapFrom": "claims.given_name",
"required": true
},
"address": {
"mapFrom": "claims.address.formatted"
},
"picture": {
"mapFrom": "claims.picture",
"defaultValue": "http://example.edu/img/placeholder.png"
},
"badge": {
"defaultValue": "http://example.edu/img/badge.png"
},
"providerSubjectId": {
"mapFrom": "authenticationProvider.subjectId"
}
},
"expiresIn": {
"months": 3
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a JSON credentials configuration
## Endpoint
```
GET /v2/credentials/web-semantic/configurations/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/web-semantic/configurations/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieve a JSON credentials configuration by providing its ID.
### **Analytic events**
* CREDENTIAL_WEB_SEMANTIC_CREDENTIAL_CONFIGURATION_RETRIEVE_START
* CREDENTIAL_WEB_SEMANTIC_CREDENTIAL_CONFIGURATION_RETRIEVE_SUCCESS
* CREDENTIAL_WEB_SEMANTIC_CREDENTIAL_CONFIGURATION_RETRIEVE_FAIL
### Responses
#### 200 - JSON credentials configuration retrieved
```json
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"name": "Course credential",
"description": "This credential shows that the person has attended a course.",
"type": "CourseCredential",
"additionalTypes": [
"AlumniCredential",
"EducationCredential"
],
"contexts": [
"https://optionalschema.example/"
],
"issuer": {
"name": "ABC University",
"logoUrl": "https://example.edu/img/logo.png",
"iconUrl": "https://example.edu/img/icon.png"
},
"proofType": "Ed25519Signature2018",
"credentialBranding": {
"backgroundColor": "#B00AA0",
"watermarkImageUrl": "https://example.edu/img/watermark.png"
},
"claimMappings": {
"firstName": {
"mapFrom": "claims.given_name",
"required": true
},
"address": {
"mapFrom": "claims.address.formatted"
},
"picture": {
"mapFrom": "claims.picture",
"defaultValue": "http://example.edu/img/placeholder.png"
},
"badge": {
"defaultValue": "http://example.edu/img/badge.png"
},
"providerSubjectId": {
"mapFrom": "authenticationProvider.subjectId"
}
},
"expiresIn": {
"months": 3
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a JSON credentials configuration
## Endpoint
```
DELETE /v2/credentials/web-semantic/configurations/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/web-semantic/configurations/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Deletes an existing JSON credentials configuration by providing its ID.
### **Analytic events**
* CREDENTIAL_WEB_SEMANTIC_CREDENTIAL_CONFIGURATION_DELETE_START
* CREDENTIAL_WEB_SEMANTIC_CREDENTIAL_CONFIGURATION_DELETE_SUCCESS
* CREDENTIAL_WEB_SEMANTIC_CREDENTIAL_CONFIGURATION_DELETE_FAIL
### Responses
#### 204 - JSON credentials configuration deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - The credential configuration is not found
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a JSON credentials configuration
## Endpoint
```
PUT /v2/credentials/web-semantic/configurations/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v2/credentials/web-semantic/configurations/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer
## Description
Updates an existing JSON credentials configuration by providing its ID.
### **Analytic events**
* CREDENTIAL_WEB_SEMANTIC_CREDENTIAL_CONFIGURATION_UPDATE_START
* CREDENTIAL_WEB_SEMANTIC_CREDENTIAL_CONFIGURATION_UPDATE_SUCCESS
* CREDENTIAL_WEB_SEMANTIC_CREDENTIAL_CONFIGURATION_UPDATE_FAIL
### Request Body
Update a credential configuration
```json
{
"name": "Course credential",
"description": "This credential shows that the person has attended a course.",
"type": "CourseCredential",
"additionalTypes": [
"AlumniCredential",
"EducationCredential"
],
"contexts": [
"https://optionalschema.example/"
],
"issuer": {
"name": "ABC University",
"logoUrl": "https://example.edu/img/logo.png",
"iconUrl": "https://example.edu/img/icon.png"
},
"proofType": "Ed25519Signature2018",
"credentialBranding": {
"backgroundColor": "#B00AA0",
"watermarkImageUrl": "https://example.edu/img/watermark.png"
},
"claimMappings": {
"firstName": {
"mapFrom": "claims.given_name",
"required": true
},
"address": {
"mapFrom": "claims.address.formatted"
},
"picture": {
"mapFrom": "claims.picture",
"defaultValue": "http://example.edu/img/placeholder.png"
},
"badge": {
"defaultValue": "http://example.edu/img/badge.png"
},
"providerSubjectId": {
"mapFrom": "authenticationProvider.subjectId"
}
},
"expiresIn": {
"months": 3
}
}
```
### Responses
#### 200 - JSON credentials configuration updated
```json
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"name": "Course credential",
"description": "This credential shows that the person has attended a course.",
"type": "CourseCredential",
"additionalTypes": [
"AlumniCredential",
"EducationCredential"
],
"contexts": [
"https://optionalschema.example/"
],
"issuer": {
"name": "ABC University",
"logoUrl": "https://example.edu/img/logo.png",
"iconUrl": "https://example.edu/img/icon.png"
},
"proofType": "Ed25519Signature2018",
"credentialBranding": {
"backgroundColor": "#B00AA0",
"watermarkImageUrl": "https://example.edu/img/watermark.png"
},
"claimMappings": {
"firstName": {
"mapFrom": "claims.given_name",
"required": true
},
"address": {
"mapFrom": "claims.address.formatted"
},
"picture": {
"mapFrom": "claims.picture",
"defaultValue": "http://example.edu/img/placeholder.png"
},
"badge": {
"defaultValue": "http://example.edu/img/badge.png"
},
"providerSubjectId": {
"mapFrom": "authenticationProvider.subjectId"
}
},
"expiresIn": {
"months": 3
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all holder applications
## Endpoint
```
GET /v1/holder/applications
```
Full URL: `https://example.vii.au01.mattr.global/v1/holder/applications`
### Authorization
Bearer token required.
Required roles: admin, holder
## Description
Retrieves all configured holder applications.
### **Analytic events**
* CREDENTIAL_HOLDER_APPLICATION_RETRIEVE_LIST_START
* CREDENTIAL_HOLDER_APPLICATION_RETRIEVE_LIST_SUCCESS
* CREDENTIAL_HOLDER_APPLICATION_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Holder applications retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM",
"data": [
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c",
"name": "Example Holder Application",
"clientId": "example-wallet-client"
}
]
}
```
# Create a holder application
## Endpoint
```
POST /v1/holder/applications
```
Full URL: `https://example.vii.au01.mattr.global/v1/holder/applications`
### Authorization
Bearer token required.
Required roles: admin, holder
## Description
Creates a holder application. Once created, application instances can be registered and interact with the MATTR VII platform (for example, to claim credentials).
### **Analytic events**
* CREDENTIAL_HOLDER_APPLICATION_CREATE_START
* CREDENTIAL_HOLDER_APPLICATION_CREATE_SUCCESS
* CREDENTIAL_HOLDER_APPLICATION_CREATE_FAIL
### Request Body
Holder application payload
```json
{
"name": "My Holder Application",
"clientId": "example-wallet-client"
}
```
### Responses
#### 201 - Holder application created
```json
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c",
"name": "Example Holder Application",
"clientId": "example-wallet-client"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a holder application
## Endpoint
```
GET /v1/holder/applications/{applicationId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/holder/applications/{applicationId}`
### Authorization
Bearer token required.
Required roles: admin, holder
## Description
Retrieves an existing holder application.
### **Analytic events**
* CREDENTIAL_HOLDER_APPLICATION_RETRIEVE_START
* CREDENTIAL_HOLDER_APPLICATION_RETRIEVE_SUCCESS
* CREDENTIAL_HOLDER_APPLICATION_RETRIEVE_FAIL
### Responses
#### 200 - Holder application retrieved
```json
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c",
"name": "Example Holder Application",
"clientId": "example-wallet-client"
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a holder application
## Endpoint
```
DELETE /v1/holder/applications/{applicationId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/holder/applications/{applicationId}`
### Authorization
Bearer token required.
Required roles: admin, holder
## Description
Deletes an existing holder application. Once deleted, any associated application instances will no longer be able to interact with the MATTR VII platform.
### **Analytic events**
* CREDENTIAL_HOLDER_APPLICATION_DELETE_START
* CREDENTIAL_HOLDER_APPLICATION_DELETE_SUCCESS
* CREDENTIAL_HOLDER_APPLICATION_DELETE_FAIL
### Responses
#### 204 - Holder application deleted
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a holder application
## Endpoint
```
PUT /v1/holder/applications/{applicationId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/holder/applications/{applicationId}`
### Authorization
Bearer token required.
Required roles: admin, holder
## Description
Updates an existing holder application.
### **Analytic events**
* CREDENTIAL_HOLDER_APPLICATION_UPDATE_START
* CREDENTIAL_HOLDER_APPLICATION_UPDATE_SUCCESS
* CREDENTIAL_HOLDER_APPLICATION_UPDATE_FAIL
### Request Body
Holder application payload
```json
{
"name": "My Holder Application",
"clientId": "example-wallet-client"
}
```
### Responses
#### 200 - Holder application updated
```json
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c",
"name": "Example Holder Application",
"clientId": "example-wallet-client"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all registered holder application instances
## Endpoint
```
GET /v1/holder/applications/{applicationId}/instances
```
Full URL: `https://example.vii.au01.mattr.global/v1/holder/applications/{applicationId}/instances`
### Authorization
Bearer token required.
Required roles: admin, holder
## Description
Retrieves all registered instances of a holder application.
This can be used by application owners to view all registered instances for a given holder application.
### **Analytic events**
* CREDENTIAL_HOLDER_APPLICATION_INSTANCE_RETRIEVE_LIST_START
* CREDENTIAL_HOLDER_APPLICATION_INSTANCE_RETRIEVE_LIST_SUCCESS
* CREDENTIAL_HOLDER_APPLICATION_INSTANCE_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Holder application instances retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM",
"data": [
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c",
"appAttestationType": "key_attestation",
"registeredAt": "2023-10-05T14:48:00.000Z",
"licenseExpiresAt": "2024-10-05T14:48:00.000Z",
"lastAttestedAt": "2023-12-01T10:30:00.000Z",
"externalReferenceId": "external-ref-12345",
"deviceDetails": {
"deviceModel": "iPhone 12",
"deviceMake": "Apple",
"osVersion": "iOS 14.4"
},
"sdkDetails": {
"sdkVersion": "1.2.3"
}
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a registered holder application instance
## Endpoint
```
GET /v1/holder/applications/{applicationId}/instances/{instanceId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/holder/applications/{applicationId}/instances/{instanceId}`
### Authorization
Bearer token required.
Required roles: admin, holder
## Description
Retrieves a registered instance of a holder application.
### **Analytic events**
* CREDENTIAL_HOLDER_APPLICATION_INSTANCE_RETRIEVE_START
* CREDENTIAL_HOLDER_APPLICATION_INSTANCE_RETRIEVE_SUCCESS
* CREDENTIAL_HOLDER_APPLICATION_INSTANCE_RETRIEVE_FAIL
### Responses
#### 200 - Holder application instance retrieved
```json
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c",
"appAttestationType": "key_attestation",
"registeredAt": "2023-10-05T14:48:00.000Z",
"licenseExpiresAt": "2024-10-05T14:48:00.000Z",
"lastAttestedAt": "2023-12-01T10:30:00.000Z",
"externalReferenceId": "external-ref-12345",
"deviceDetails": {
"deviceModel": "iPhone 12",
"deviceMake": "Apple",
"osVersion": "iOS 14.4"
},
"sdkDetails": {
"sdkVersion": "1.2.3"
}
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a registered holder application instance
## Endpoint
```
DELETE /v1/holder/applications/{applicationId}/instances/{instanceId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/holder/applications/{applicationId}/instances/{instanceId}`
### Authorization
Bearer token required.
Required roles: admin, holder
## Description
Deletes a registered instance of a holder application.
Deleted instances will no longer be able to interact with the platform or receive tokens, and any existing tokens will be revoked.
Application owners can use this endpoint to remove individual instances that are no longer needed or were registered by mistake, without affecting the entire holder application or its other instances. This is useful for cleaning up test instances or managing specific devices, rather than deleting the whole application and all its associated instances.
### **Analytic events**
* CREDENTIAL_HOLDER_APPLICATION_INSTANCE_DELETE_START
* CREDENTIAL_HOLDER_APPLICATION_INSTANCE_DELETE_SUCCESS
* CREDENTIAL_HOLDER_APPLICATION_INSTANCE_DELETE_FAIL
### Responses
#### 204 - Holder application instance deleted
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all holder root CA certificates
## Endpoint
```
GET /v1/holder/certificates/ca
```
Full URL: `https://example.vii.au01.mattr.global/v1/holder/certificates/ca`
### Authorization
Bearer token required.
Required roles: admin, holder
## Description
Retrieves all holder root CA certificates for the tenant.
### **Analytic events**
* CREDENTIAL_HOLDER_CA_CERTIFICATE_RETRIEVE_LIST_START
* CREDENTIAL_HOLDER_CA_CERTIFICATE_RETRIEVE_LIST_SUCCESS
* CREDENTIAL_HOLDER_CA_CERTIFICATE_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Holder root CA certificates retrieved.
```json
{
"data": [
{
"id": "281d20b3-42a3-40dd-b29a-115ff32b02b7",
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"certificateFingerprint": "a3b2c1d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890",
"certificateData": {
"commonName": "Example Tenant Wallet Attestation Root",
"country": "NZ",
"notBefore": "2026-04-06T00:00:00.000Z",
"notAfter": "2036-04-06T00:00:00.000Z"
},
"isManaged": true
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create a holder root CA certificate
## Endpoint
```
POST /v1/holder/certificates/ca
```
Full URL: `https://example.vii.au01.mattr.global/v1/holder/certificates/ca`
### Authorization
Bearer token required.
Required roles: admin, holder
## Description
Creates a holder root CA certificate that is used to issue wallet attestation signer certificates.
Two flows are supported:
- **Managed** — MATTR VII generates the root certificate and manages the private key on the customer's behalf. Supply no `certificatePem` in the request body; `commonName` and `country` are optional.
- **Unmanaged** — the customer supplies their own externally-managed root CA in PEM format. `commonName` and `country` are extracted from the certificate and must not be provided in the request.
A maximum of three holder root CA certificates can be created per tenant. Only one can be active at a time.
The newly-created root is always inactive. Activate it by issuing `PUT /v1/holder/certificates/ca/{certificateId}` with `{ "active": true }`, which also deactivates any previously active root for the tenant (single-active constraint).
### **Analytic events**
* CREDENTIAL_HOLDER_CA_CERTIFICATE_CREATE_START
* CREDENTIAL_HOLDER_CA_CERTIFICATE_CREATE_SUCCESS
* CREDENTIAL_HOLDER_CA_CERTIFICATE_CREATE_FAIL
### Request Body
### Responses
#### 201 - Holder root CA certificate created.
```json
{
"id": "281d20b3-42a3-40dd-b29a-115ff32b02b7",
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"certificateFingerprint": "a3b2c1d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890",
"certificateData": {
"commonName": "Example Tenant Wallet Attestation Root",
"country": "NZ",
"notBefore": "2026-04-06T00:00:00.000Z",
"notAfter": "2036-04-06T00:00:00.000Z"
},
"isManaged": true
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 409 - Maximum number of holder root CA certificates reached. Delete an existing certificate before creating a new one.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 503 - Service Unavailable. The server is temporarily unavailable to handle requests.
# Retrieve a holder root CA certificate
## Endpoint
```
GET /v1/holder/certificates/ca/{certificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/holder/certificates/ca/{certificateId}`
### Authorization
Bearer token required.
Required roles: admin, holder
## Description
Retrieves a holder root CA certificate by ID.
### **Analytic events**
* CREDENTIAL_HOLDER_CA_CERTIFICATE_RETRIEVE_START
* CREDENTIAL_HOLDER_CA_CERTIFICATE_RETRIEVE_SUCCESS
* CREDENTIAL_HOLDER_CA_CERTIFICATE_RETRIEVE_FAIL
### Responses
#### 200 - Holder root CA certificate retrieved.
```json
{
"id": "281d20b3-42a3-40dd-b29a-115ff32b02b7",
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"certificateFingerprint": "a3b2c1d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890",
"certificateData": {
"commonName": "Example Tenant Wallet Attestation Root",
"country": "NZ",
"notBefore": "2026-04-06T00:00:00.000Z",
"notAfter": "2036-04-06T00:00:00.000Z"
},
"isManaged": true
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a holder root CA certificate
## Endpoint
```
DELETE /v1/holder/certificates/ca/{certificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/holder/certificates/ca/{certificateId}`
### Authorization
Bearer token required.
Required roles: admin, holder
## Description
Deletes a holder root CA certificate and cascade-deletes all associated signer certificates. For managed roots and signers, MATTR VII also removes the private key material it was holding on the customer's behalf.
Deletion does not invalidate any wallet attestation JWTs that were issued under this root. Those JWTs remain cryptographically valid until their natural expiry — verifiers that have already cached the signer's public key may continue to accept them. CRL-based revocation of issued attestations is not supported in this release.
### **Analytic events**
* CREDENTIAL_HOLDER_CA_CERTIFICATE_DELETE_START
* CREDENTIAL_HOLDER_CA_CERTIFICATE_DELETE_SUCCESS
* CREDENTIAL_HOLDER_CA_CERTIFICATE_DELETE_FAIL
### Responses
#### 204 - Holder root CA certificate deleted.
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 503 - Service Unavailable. The server is temporarily unavailable to handle requests.
# Update a holder root CA certificate
## Endpoint
```
PUT /v1/holder/certificates/ca/{certificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/holder/certificates/ca/{certificateId}`
### Authorization
Bearer token required.
Required roles: admin, holder
## Description
Updates a holder root CA certificate. The only mutable field is `active`.
Setting `active: true` deactivates all other roots for the tenant (single-active constraint).
### **Analytic events**
* CREDENTIAL_HOLDER_CA_CERTIFICATE_UPDATE_START
* CREDENTIAL_HOLDER_CA_CERTIFICATE_UPDATE_SUCCESS
* CREDENTIAL_HOLDER_CA_CERTIFICATE_UPDATE_FAIL
### Request Body
```json
{
"active": true
}
```
### Responses
#### 200 - Holder root CA certificate updated.
```json
{
"id": "281d20b3-42a3-40dd-b29a-115ff32b02b7",
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"certificateFingerprint": "a3b2c1d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890",
"certificateData": {
"commonName": "Example Tenant Wallet Attestation Root",
"country": "NZ",
"notBefore": "2026-04-06T00:00:00.000Z",
"notAfter": "2036-04-06T00:00:00.000Z"
},
"isManaged": true
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a holder root CA certificate revocation list
## Endpoint
```
GET /v1/holder/certificates/ca/{certificateId}/crl
```
Full URL: `https://example.vii.au01.mattr.global/v1/holder/certificates/ca/{certificateId}/crl`
### Authorization
None required.
## Description
Retrieves the Certificate Revocation List (CRL) for a managed holder root CA certificate, as a DER-encoded binary document.
This endpoint is only available for managed roots — for unmanaged roots it returns `404 NoCertificateRevocationList`.
### Responses
#### 200 - Holder root CA certificate revocation list retrieved.
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 503 - Service Unavailable. The server is temporarily unavailable to handle requests.
# Retrieve all wallet attestation signers
## Endpoint
```
GET /v1/holder/certificates/wallet-attestation-signers
```
Full URL: `https://example.vii.au01.mattr.global/v1/holder/certificates/wallet-attestation-signers`
### Authorization
Bearer token required.
Required roles: admin
## Description
Retrieves all wallet attestation signers for the tenant across all roots.
The response may contain a mix of:
- CSR-pending signers (unmanaged, certificate not yet uploaded)
- Active signers (managed or unmanaged with an uploaded certificate)
### **Analytic events**
* CREDENTIAL_HOLDER_WALLET_ATTESTATION_SIGNER_CERTIFICATE_RETRIEVE_LIST_START
* CREDENTIAL_HOLDER_WALLET_ATTESTATION_SIGNER_CERTIFICATE_RETRIEVE_LIST_SUCCESS
* CREDENTIAL_HOLDER_WALLET_ATTESTATION_SIGNER_CERTIFICATE_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Wallet attestation signers retrieved.
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create a wallet attestation signer
## Endpoint
```
POST /v1/holder/certificates/wallet-attestation-signers
```
Full URL: `https://example.vii.au01.mattr.global/v1/holder/certificates/wallet-attestation-signers`
### Authorization
Bearer token required.
Required roles: admin
## Description
Creates a wallet attestation signer for an unmanaged root CA and returns its Certificate Signing Request (CSR).
This endpoint is only available for unmanaged roots — managed root signers are auto-provisioned on demand during the first wallet attestation request and never need to be created explicitly.
The returned signer is created with `active: false`; use the CSR to obtain a signed certificate externally and upload it via `PUT /v1/holder/certificates/wallet-attestation-signers/{certificateId}` to activate the signer.
A maximum of five wallet attestation signers can be created per root.
### **Analytic events**
* CREDENTIAL_HOLDER_WALLET_ATTESTATION_SIGNER_CERTIFICATE_CREATE_START
* CREDENTIAL_HOLDER_WALLET_ATTESTATION_SIGNER_CERTIFICATE_CREATE_SUCCESS
* CREDENTIAL_HOLDER_WALLET_ATTESTATION_SIGNER_CERTIFICATE_CREATE_FAIL
### Request Body
```json
{
"caId": "281d20b3-42a3-40dd-b29a-115ff32b02b7"
}
```
### Responses
#### 201 - Wallet attestation signer created.
```json
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"caId": "281d20b3-42a3-40dd-b29a-115ff32b02b7",
"csrPem": "-----BEGIN CERTIFICATE REQUEST-----\nMIIC5zCCAc8CAQAwgaExC...\n-----END CERTIFICATE REQUEST-----",
"active": false
}
```
#### 400 - Bad request. The request was malformed, missing required parameters, or the target root is managed (managed signers are auto-provisioned and cannot be created via this endpoint).
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 409 - Maximum number of wallet attestation signers for this root has been reached.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 503 - Service Unavailable. The server is temporarily unavailable to handle requests.
# Retrieve a wallet attestation signer
## Endpoint
```
GET /v1/holder/certificates/wallet-attestation-signers/{certificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/holder/certificates/wallet-attestation-signers/{certificateId}`
### Authorization
Bearer token required.
Required roles: admin
## Description
Retrieves a wallet attestation signer by ID. The response shape depends on the signer's state — CSR-pending signers return a CSR, signers with a signed certificate return the full certificate details.
### **Analytic events**
* CREDENTIAL_HOLDER_WALLET_ATTESTATION_SIGNER_CERTIFICATE_RETRIEVE_START
* CREDENTIAL_HOLDER_WALLET_ATTESTATION_SIGNER_CERTIFICATE_RETRIEVE_SUCCESS
* CREDENTIAL_HOLDER_WALLET_ATTESTATION_SIGNER_CERTIFICATE_RETRIEVE_FAIL
### Responses
#### 200 - Wallet attestation signer retrieved.
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a wallet attestation signer
## Endpoint
```
DELETE /v1/holder/certificates/wallet-attestation-signers/{certificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/holder/certificates/wallet-attestation-signers/{certificateId}`
### Authorization
Bearer token required.
Required roles: admin
## Description
Deletes a wallet attestation signer. For managed signers, MATTR VII also removes the private key material it was holding on the customer's behalf.
### **Analytic events**
* CREDENTIAL_HOLDER_WALLET_ATTESTATION_SIGNER_CERTIFICATE_DELETE_START
* CREDENTIAL_HOLDER_WALLET_ATTESTATION_SIGNER_CERTIFICATE_DELETE_SUCCESS
* CREDENTIAL_HOLDER_WALLET_ATTESTATION_SIGNER_CERTIFICATE_DELETE_FAIL
### Responses
#### 204 - Wallet attestation signer deleted.
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 503 - Service Unavailable. The server is temporarily unavailable to handle requests.
# Update a wallet attestation signer
## Endpoint
```
PUT /v1/holder/certificates/wallet-attestation-signers/{certificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/holder/certificates/wallet-attestation-signers/{certificateId}`
### Authorization
Bearer token required.
Required roles: admin
## Description
Updates a wallet attestation signer by:
- Uploading a signed certificate PEM that matches the signer's CSR (first-time upload for CSR-pending signers only).
- Activating or deactivating the signer. Only signers with a valid PEM certificate can be activated.
`certificatePem` is immutable after the first upload — subsequent PUT requests may only toggle `active`.
### **Analytic events**
* CREDENTIAL_HOLDER_WALLET_ATTESTATION_SIGNER_CERTIFICATE_UPDATE_START
* CREDENTIAL_HOLDER_WALLET_ATTESTATION_SIGNER_CERTIFICATE_UPDATE_SUCCESS
* CREDENTIAL_HOLDER_WALLET_ATTESTATION_SIGNER_CERTIFICATE_UPDATE_FAIL
### Request Body
```json
{
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----"
}
```
### Responses
#### 200 - Wallet attestation signer updated.
```json
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"caId": "281d20b3-42a3-40dd-b29a-115ff32b02b7",
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"certificateFingerprint": "f6cad6e579d70b3973efa60624af731a580d1a11a7579e70f2f10f059dc86172",
"certificateData": {
"commonName": "example.com Wallet Attestation Signer",
"country": "NZ",
"notBefore": "2026-04-06T00:00:00.000Z",
"notAfter": "2027-04-06T00:00:00.000Z"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Request authorization for access to resources
## Endpoint
```
GET /v1/oauth/authorize
```
Full URL: `https://example.vii.au01.mattr.global/v1/oauth/authorize`
### Authorization
None required.
## Description
This endpoint is used to request authorization from the user for access to the requested resources. After the user approves the request, an authorization code is returned to the client.
See [https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-authorization-endpoint](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-authorization-endpoint)
See [https://www.rfc-editor.org/rfc/rfc6749.html#section-3.1](https://www.rfc-editor.org/rfc/rfc6749.html#section-3.1)
### **Analytic events**
* OPENID_AUTHORIZE_START
* OPENID_AUTHORIZE_SUCCESS
* OPENID_AUTHORIZE_FAIL
### Query Parameters
- `response_type`: string (required)
The response type, which must be 'code'.
- `client_id`: string (required)
The client identifier.
- `redirect_uri`: string (required)
The URI to which the authorization server will redirect the user-agent with the authorization code.
- `scope`: string (required)
The scope of the access request.
- `state`: string
An opaque value used by the client to maintain state between the request and callback.
- `code_challenge_method`: string (required)
The method used to derive the code_challenge, which must be 'S256'.
- `code_challenge`: string (required)
A high entropy random challenge generated by the client.
### Responses
#### 302 - Redirection to client application with authorization code
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 401 - Unauthorized. The client is not recognized by authorization server.
#### 403 - Forbidden. The client is recognized by authorization server but is not allowed to access this resource.
#### 500 - Internal Server Error. An unexpected error occurred.
# Exchange authorization code for access token
## Endpoint
```
POST /v1/oauth/token
```
Full URL: `https://example.vii.au01.mattr.global/v1/oauth/token`
### Authorization
None required.
## Description
This endpoint is used to exchange an authorization code or a pre-authorized code for an access token, which is later used to request a credential.
- In an Authorization Code flow the authorization code is obtained from the authorization endpoint after the user has successfully authenticated.
- In a Pre-authorized Code flow the pre-authorized code is obtained from the offer URI.
See [https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-token-endpoint](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-token-endpoint) for more information.
### **Analytic events**
* OPENID_TOKEN_START
* OPENID_TOKEN_SUCCESS
* OPENID_TOKEN_FAIL
### Request Body
### Responses
#### 200 - Access token successfully returned.
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 401 - Unauthorized. The client is not recognized by authorization server.
#### 403 - Forbidden. The client is recognized by authorization server but is not allowed to access this resource.
#### 500 - Internal Server Error. An unexpected error occurred.
# Issue a verifiable credential
## Endpoint
```
POST /v1/openid/credential
```
Full URL: `https://example.vii.au01.mattr.global/v1/openid/credential`
### Authorization
Bearer token required.
## Description
Issues a credential to a holder upon presentation of a valid access token, as per [OID4VCI](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-credential-endpoint).
Supports [encrypted](/docs/issuance/credential-issuance/e2e-encryption) and non-encrypted credential issuance.
Encrypted credential issuance is currently in technical preview and must be enabled on a per-tenant basis. If you would like to enable this feature for your tenant, please [contact us](mailto:dev-support@mattr.global).
For non-encrypted credential issuance the valid access token must be provided in one of the following header formats:
**Bearer**
- Authorization: `Bearer `.
- Content-Type: `application/json`
- Body: ``
OR
**DPoP**
Only required when using DPoP-bound access tokens.
- Authorization: `DPoP `.
- DPoP: ``
- Content-Type: `application/json`
- Body: ``
For encrypted credential issuance, [contact us](mailto:dev-support@mattr.global) to configure how your MATTR VII enforces request and/or response encryption, and then:
- For **request** encryption, `Content-Type` must be set to `application/jwt` and the payload must be JWE formatted.
- For **response** encryption, include the [`credential_response_encryption`](/docs/issuance/credential-issuance/e2e-encryption#encryption-key-provisioning) property in the raw request payload to specify encryption details.
### **Analytic events**
* OPENID_CREDENTIAL_START
* OPENID_CREDENTIAL_SUCCESS
* OPENID_CREDENTIAL_FAIL
### Request Body
### Responses
#### 200 - Credential issued
# Create an Authorization Code credential offer
## Endpoint
```
POST /v1/openid/offers
```
Full URL: `https://example.vii.au01.mattr.global/v1/openid/offers`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns an OID4VCI credential offer URI.
See [https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-10.1](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-10.1)
This offer can be used more than once, and can be shared with multiple users. Each user will authenticate independently during the credential issuance workflow, allowing the same offer URI to issue credentials with different user-specific data to multiple holders.
### **Analytic events**
* OPENID_OFFER_CREATE_START
* OPENID_OFFER_CREATE_SUCCESS
* OPENID_OFFER_CREATE_FAIL
### Request Body
```json
{
"credentials": [
"707e920a-f342-443b-ae24-6946b7b5033e"
],
"request_parameters": {
"login_hint": "user@example.com",
"prompt": "login"
}
}
```
### Responses
#### 200 - Credential offer URI created
```json
{
"uri": "openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fmyissuer.example.com%22%2C%22credentials%22%3A%5B%22707e920a-f342-443b-ae24-6946b7b5033e%22%5D%2C%22credential_configuration_ids%22%3A%5B%22707e920a-f342-443b-ae24-6946b7b5033e%22%5D%2C%22request_parameters%22%3A%7B%22login_hint%22%3A%22user%40example.com%22%2C%22prompt%22%3A%22login%22%7D%7D"
}
```
# Create a Pre-Authorized Code credential offer
## Endpoint
```
POST /v1/openid/offers/pre-authorized
```
Full URL: `https://example.vii.au01.mattr.global/v1/openid/offers/pre-authorized`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Generate a new [OID4VCI Pre-Authorized Code](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-4.1) credential offer.
This offer can only be used once. Once the offer is successfully claimed and the credential is issued, the pre-authorized code is consumed and the offer becomes invalid. The offer cannot be claimed again, even if the same user attempts to claim it. This is a security measure to prevent unauthorized credential duplication. If you need to issue another credential to the same user, you must generate a new credential offer.
The Pre-authorized Code flow is only supported for the mDocs credential format.
The total size of the request cannot exceed 500KB, including any claims. This is mostly relevant when including large claims such as images. When holders present credentials over Bluetooth Low Energy (BLE), keep payloads as small as practical, as large claims can degrade the transfer experience. Reserve larger payloads for remote presentation flows.
### **Analytic events**
* OPENID_PRE_AUTHORIZED_OFFER_CREATE_START
* OPENID_PRE_AUTHORIZED_OFFER_CREATE_SUCCESS
* OPENID_PRE_AUTHORIZED_OFFER_CREATE_FAIL
### Request Body
```json
{
"credentials": [
"707e920a-f342-443b-ae24-6946b7b5033e"
],
"transactionCodeConfiguration": {
"inputMode": "numeric",
"description": "Please enter the one-time code that was sent to you via email."
},
"claims": {
"givenName": "John",
"familyName": "Doe",
"email": "john.doe@example.com"
},
"claimsToPersist": [
"userId"
],
"expiresIn": {
"minutes": 5
}
}
```
### Responses
#### 200 - Credential offer created
```json
{
"uri": "openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fexample.com%22%2C%22credentials%22%3A%5B%222edaf985-fcc2-4448-9c8e-a04c6c7351c2%22%5D%2C%22credential_configuration_ids%22%3A%5B%222edaf985-fcc2-4448-9c8e-a04c6c7351c2%22%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22stukD6lg9c9tQ3jUCa32wVi1HI%2BQIVsFK%2FQPvC2CHRs%3D%22%2C%22tx_code%22%3A%7B%22length%22%3A6%2C%22input_mode%22%3A%22numeric%22%2C%22description%22%3A%22Please%20provide%20the%20one-time%20code%20that%20was%20sent%20via%20e-mail%22%7D%7D%7D%7D",
"expiresAt": "2025-05-01T00:01:00.000Z",
"transactionCode": 493536
}
```
# Delete a Pre-authorized Code credential offer
## Endpoint
```
DELETE /v1/openid/offers/pre-authorized/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v1/openid/offers/pre-authorized/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Delete an [OID4VCI Pre-authorized Code](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-4.1) credential offer.
### **Analytic events**
* OPENID_PRE_AUTHORIZED_OFFER_DELETE_START
* OPENID_PRE_AUTHORIZED_OFFER_DELETE_SUCCESS
* OPENID_PRE_AUTHORIZED_OFFER_DELETE_FAIL
### Responses
#### 204 - Credential offer deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve OID4VCI issuer metadata
## Endpoint
```
GET /.well-known/openid-credential-issuer
```
Full URL: `https://example.vii.au01.mattr.global/.well-known/openid-credential-issuer`
### Authorization
None required.
## Description
Returns OID4VCI issuer metadata. This is the standard OID4VCI Well Known endpoint for your tenant.
This endpoint is unprotected, public facing and can be deterministically found at the root of the tenant subdomain or alias by any party wishing to discover the OID4VCI capabilities.
### Responses
#### 200 - OID4VCI credential issuer metadata retrieved
```json
{
"scopes_supported": [
"ldp_vc:ExampleCredential"
],
"response_types_supported": [
"code"
],
"response_modes_supported": [
"query"
],
"grant_types_supported": [
"authorization_code"
],
"code_challenge_methods_supported": [
"S256"
],
"credential_configurations_supported": {
"2cdb2c15-39a7-4556-abab-4515ce2d831b": {
"format": "ldp_vc",
"id": "2cdb2c15-39a7-4556-abab-4515ce2d831b",
"scope": "ldp_vc:TestCredential",
"credential_definition": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://schema.org"
],
"type": [
"VerifiableCredential",
"TestCredential"
]
},
"credential_signing_alg_values_supported": [
"Ed25519Signature2018",
"BbsSignatureProof2022"
],
"cryptographic_binding_methods_supported": [
"did:key"
],
"proof_types_supported": {
"jwt": {
"proof_signing_alg_values_supported": [
"EdDSA"
]
}
},
"credential_metadata": {
"display": [
{
"name": "Test Credential",
"logo": {
"uri": "https://example.com/logo.png",
"alt_text": "Example Logo"
},
"locale": "en-US",
"background_color": "#FFFFFF",
"text_color": "#000000"
}
],
"claims": [
{
"path": [
"credentialSubject",
"firstName"
],
"mandatory": true,
"display": [
{
"name": "First Name",
"locale": "en-US"
}
]
}
]
}
},
"3dfe1c4a-5b6c-4e2f-9f3a-2b1c4d5e6f7g": {
"format": "cwt_vc",
"id": "3dfe1c4a-5b6c-4e2f-9f3a-2b1c4d5e6f7g",
"scope": "cwt_vc:TestCredential",
"types": [
"VerifiableCredential",
"TestCredential"
],
"cryptographic_binding_methods_supported": [],
"credential_signing_alg_values_supported": [
-7
],
"credential_metadata": {
"claims": [
{
"path": [
"vc",
"credentialSubject",
"firstName"
],
"mandatory": true,
"display": [
{
"name": "First Name",
"locale": "en-US"
}
]
}
]
}
},
"b068c060-cc72-4758-9526-92d29edb821f": {
"format": "cwt",
"id": "b068c060-cc72-4758-9526-92d29edb821f",
"scope": "cwt:TestCredential",
"type": "TestCredential",
"cryptographic_binding_methods_supported": [],
"credential_signing_alg_values_supported": [
-7
],
"credential_metadata": {
"claims": [
{
"path": [
"firstName"
],
"mandatory": true,
"display": [
{
"name": "First Name",
"locale": "en-US"
}
]
}
]
}
},
"a1b2c3d4-e5f6-4789-abcd-ef0123456789": {
"format": "mso_mdoc",
"doctype": "org.iso.18013.5.1.mDL.T",
"scope": "mso_mdoc:TestCredential",
"id": "a1b2c3d4-e5f6-4789-abcd-ef0123456789",
"cryptographic_binding_methods_supported": [
"mso"
],
"credential_signing_alg_values_supported": [
-7
],
"proof_types_supported": {
"jwt": {
"proof_signing_alg_values_supported": [
"ES256"
]
}
},
"credential_metadata": {
"claims": [
{
"path": [
"org.iso.18013.5.1",
"firstName"
],
"mandatory": true,
"display": [
{
"name": "First Name",
"locale": "en-US"
}
]
}
],
"display": [
{
"name": "Test Mobile Credential",
"logo": {
"uri": "https://example.com/logo.png",
"alt_text": "Example Logo"
},
"locale": "en-US",
"background_color": "#FFFFFF",
"text_color": "#000000"
}
]
}
}
},
"credential_response_encryption": {
"alg_values_supported": [
"HPKE-7"
],
"enc_values_supported": [
"A256GCM"
],
"encryption_required": false
},
"credential_request_encryption": {
"jwks": {
"keys": [
{
"kty": "EC",
"kid": "kid",
"use": "enc",
"crv": "P-256",
"alg": "HPKE-7",
"x": "YO4epjifD-KWeq1sL2tNmm36BhXnkJ0He-WqMYrp9Fk",
"y": "Hekpm0zfK7C-YccH5iBjcIXgf6YdUvNUac_0At55Okk"
}
]
},
"enc_values_supported": [
"A256GCM"
],
"encryption_required": false
}
}
```
# Retrieve authorization server metadata
## Endpoint
```
GET /.well-known/oauth-authorization-server
```
Full URL: `https://example.vii.au01.mattr.global/.well-known/oauth-authorization-server`
### Authorization
None required.
## Description
Returns the OAuth 2.0 Authorization Server Metadata for your tenant.
This is the standard OAuth 2.0 Well-Known endpoint, providing public metadata that describes the tenant’s OAuth 2.0 configuration and capabilities.
https://www.rfc-editor.org/rfc/rfc8414.html
This endpoint is intended for public consumption, and as such does not require authentication.
### Responses
#### 200 - OAuth authorization server metadata
```json
{
"scopes_supported": [
"ldp_vc:ExampleCredential",
"ldp_vc:CourseCredential",
"cwt:CourseCredential",
"cwt_vc:CourseCredential",
"mso_mdoc:org.iso.18013.5.1.mDL"
],
"response_types_supported": [
"code"
],
"response_modes_supported": [
"query",
"fragment"
],
"grant_types_supported": [
"authorization_code"
],
"token_endpoint_auth_methods_supported": [
"client_secret_basic",
"none"
],
"code_challenge_methods_supported": [
"S256"
],
"dpop_signing_alg_values_supported": [
"ES256"
]
}
```
# Retrieve all claims sources
## Endpoint
```
GET /v1/claim-sources
```
Full URL: `https://example.vii.au01.mattr.global/v1/claim-sources`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns a list of all claims sources configured on your tenant.
### **Analytic event**
* CLAIM_SOURCE_RETRIEVE_LIST_START
* CLAIM_SOURCE_RETRIEVE_LIST_SUCCESS
* CLAIM_SOURCE_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number
Range size of returned entries, default 100
- `cursor`: string
Starting point for the range of entries
### Responses
#### 200 - Claims sources retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM",
"data": [
{
"id": "57fa09e2-82f3-4d3d-9eca-d0253e84a4e6",
"name": "My claims from example.com",
"url": "https://example.com"
}
]
}
```
#### 400 - Bad Request
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Configure a claims source
## Endpoint
```
POST /v1/claim-sources
```
Full URL: `https://example.vii.au01.mattr.global/v1/claim-sources`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Configures a new claims source for your tenant. When issuing a new credential, MATTR VII will make either a GET or a POST request to the claims source using the configured request parameters and fetch available data. This fetched data can then be included in the issued credential.
### **Analytic event**
* CLAIM_SOURCE_CREATE_START
* CLAIM_SOURCE_CREATE_SUCCESS
* CLAIM_SOURCE_CREATE_FAIL
### Request Body
The claim source payload
```json
{
"name": "My claims from example.com",
"url": "https://example.com"
}
```
### Responses
#### 201 - Claims source configured
```json
{
"id": "57fa09e2-82f3-4d3d-9eca-d0253e84a4e6",
"name": "My claims from example.com",
"url": "https://example.com"
}
```
#### 400 - Bad Request
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a claims source
## Endpoint
```
GET /v1/claim-sources/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v1/claim-sources/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves an existing claims source by providing its `claimSourceID`.
### **Analytic event**
* CLAIM_SOURCE_RETRIEVE_START
* CLAIM_SOURCE_RETRIEVE_SUCCESS
* CLAIM_SOURCE_RETRIEVE_FAIL
### Responses
#### 200 - Claims source retrieved
```json
{
"id": "57fa09e2-82f3-4d3d-9eca-d0253e84a4e6",
"name": "My claims from example.com",
"url": "https://example.com"
}
```
#### 400 - Bad Request
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Claims source not found
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a claims source
## Endpoint
```
DELETE /v1/claim-sources/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v1/claim-sources/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Deletes an existing claims source by providing its `claimSourceID`.
### **Analytic event**
* CLAIM_SOURCE_DELETE_START
* CLAIM_SOURCE_DELETE_SUCCESS
* CLAIM_SOURCE_DELETE_FAIL
### Responses
#### 204 - Claims source deleted
#### 400 - Bad Request
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Claims source not found
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a claims source
## Endpoint
```
PUT /v1/claim-sources/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v1/claim-sources/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Updates an existing claim source by providing its `claimSourceID`.
### **Analytic event**
* CLAIM_SOURCE_UPDATE_START
* CLAIM_SOURCE_UPDATE_SUCCESS
* CLAIM_SOURCE_UPDATE_FAIL
### Request Body
The updated claims source payload
```json
{
"name": "My claims from example.com",
"url": "https://example.com"
}
```
### Responses
#### 200 - Claims source updated
```json
{
"id": "57fa09e2-82f3-4d3d-9eca-d0253e84a4e6",
"name": "My claims from example.com",
"url": "https://example.com"
}
```
#### 400 - Bad Request
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Claims source not found
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# List inboxes
## Endpoint
```
GET /v1/messaging/inboxes
```
Full URL: `https://example.vii.au01.mattr.global/v1/messaging/inboxes`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns a list of all inboxes on the tenant.
### **Analytic events**
* MESSAGING_INBOX_RETRIEVE_LIST_START
* MESSAGING_INBOX_RETRIEVE_LIST_SUCCESS
* MESSAGING_INBOX_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - A list of inboxes
```json
{
"data": [
{
"id": "f04faabf-cea8-4f39-95b3-0ce357ac4d03",
"name": "My_Inbox"
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
# Create an inbox
## Endpoint
```
POST /v1/messaging/inboxes
```
Full URL: `https://example.vii.au01.mattr.global/v1/messaging/inboxes`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Creates an inbox that can register DIDs and then hold messages sent to those DIDs service points.
### **Analytic events**
* MESSAGING_INBOX_CREATE_START
* MESSAGING_INBOX_CREATE_SUCCESS
* MESSAGING_INBOX_CREATE_FAIL
### Request Body
Inbox configuration
```json
{
"name": "My_Inbox"
}
```
### Responses
#### 201 - Inbox created
```json
{
"id": "f04faabf-cea8-4f39-95b3-0ce357ac4d03",
"name": "My_Inbox"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve an inbox
## Endpoint
```
GET /v1/messaging/inboxes/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v1/messaging/inboxes/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves an inbox based on its ID.
### **Analytic events**
* MESSAGING_INBOX_RETRIEVE_START
* MESSAGING_INBOX_RETRIEVE_SUCCESS
* MESSAGING_INBOX_RETRIEVE_FAIL
### Responses
#### 200 - Inbox returned
```json
{
"id": "f04faabf-cea8-4f39-95b3-0ce357ac4d03",
"name": "My_Inbox"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete an inbox
## Endpoint
```
DELETE /v1/messaging/inboxes/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v1/messaging/inboxes/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Deletes an inbox by providing its ID.
### **Analytic events**
* MESSAGING_INBOX_DELETE_START
* MESSAGING_INBOX_DELETE_SUCCESS
* MESSAGING_INBOX_DELETE_FAIL
### Responses
#### 204 - Inbox deleted
#### 400 - Bad Request
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update an inbox
## Endpoint
```
PUT /v1/messaging/inboxes/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v1/messaging/inboxes/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Update the inbox configurations
### Request Body
Updates an inbox name.
### **Analytic events**
* MESSAGING_INBOX_UPDATE_START
* MESSAGING_INBOX_UPDATE_SUCCESS
* MESSAGING_INBOX_UPDATE_FAIL
### Responses
#### 200 - Inbox updated
```json
{
"id": "f04faabf-cea8-4f39-95b3-0ce357ac4d03",
"name": "My_Inbox"
}
```
#### 400 - Bad Request
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve inbox DIDs
## Endpoint
```
GET /v1/messaging/inboxes/{id}/dids
```
Full URL: `https://example.vii.au01.mattr.global/v1/messaging/inboxes/{id}/dids`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves a list of all the DIDs registered with the requested inbox.
### **Analytic events**
* MESSAGING_INBOX_DID_RETRIEVE_LIST_START
* MESSAGING_INBOX_DID_RETRIEVE_LIST_SUCCESS
* MESSAGING_INBOX_DID_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - A list of inbox DIDs
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM",
"data": [
"did:key:did1",
"did:key:did2",
"did:key:did3"
]
}
```
#### 400 - Bad Request
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Register DID with an inbox
## Endpoint
```
POST /v1/messaging/inboxes/{id}/dids
```
Full URL: `https://example.vii.au01.mattr.global/v1/messaging/inboxes/{id}/dids`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Register the provided DID to the requested inbox.
DID registration with inboxes is currently limited to `did:key`'
### **Analytic events**
* MESSAGING_INBOX_DID_REGISTER_START
* MESSAGING_INBOX_DID_REGISTER_SUCCESS
* MESSAGING_INBOX_DID_REGISTER_FAIL
### Request Body
DID registration information
### Responses
#### 201 - DID registered with inbox
#### 400 - Bad Request
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Unregister DID with an inbox
## Endpoint
```
DELETE /v1/messaging/inboxes/{id}/dids/{did}
```
Full URL: `https://example.vii.au01.mattr.global/v1/messaging/inboxes/{id}/dids/{did}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Unregisters a DID from the requested inbox.
### **Analytic events**
* MESSAGING_INBOX_DID_UNREGISTER_START
* MESSAGING_INBOX_DID_UNREGISTER_SUCCESS
* MESSAGING_INBOX_DID_UNREGISTER_FAIL
### Responses
#### 204 - DID unregistered from inbox
#### 400 - Bad Request
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all messages
## Endpoint
```
GET /v1/messaging/inboxes/{id}/messages
```
Full URL: `https://example.vii.au01.mattr.global/v1/messaging/inboxes/{id}/messages`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieving all the messages from an inbox
### **Analytic events**
* MESSAGING_INBOX_MESSAGE_RETRIEVE_LIST_START
* MESSAGING_INBOX_MESSAGE_RETRIEVE_LIST_SUCCESS
* MESSAGING_INBOX_MESSAGE_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - A list of inbox messages
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 400 - Bad Request
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a message
## Endpoint
```
GET /v1/messaging/inboxes/{id}/messages/{messageid}
```
Full URL: `https://example.vii.au01.mattr.global/v1/messaging/inboxes/{id}/messages/{messageid}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves a message from the requested inbox by providing its ID.
### **Analytic events**
* MESSAGING_INBOX_MESSAGE_RETRIEVE_START
* MESSAGING_INBOX_MESSAGE_RETRIEVE_SUCCESS
* MESSAGING_INBOX_MESSAGE_RETRIEVE_FAIL
### Responses
#### 200 - An inbox message
#### 400 - Bad Request
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a message
## Endpoint
```
DELETE /v1/messaging/inboxes/{id}/messages/{messageid}
```
Full URL: `https://example.vii.au01.mattr.global/v1/messaging/inboxes/{id}/messages/{messageid}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Deletes a message from the requested inbox by providing its ID.
### **Analytic events**
* MESSAGING_INBOX_MESSAGE_DELETE_START
* MESSAGING_INBOX_MESSAGE_DELETE_SUCCESS
* MESSAGING_INBOX_MESSAGE_DELETE_FAIL
### Responses
#### 204 - Message deleted
#### 400 - Bad Request
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Sign a message
## Endpoint
```
POST /v1/messaging/sign
```
Full URL: `https://example.vii.au01.mattr.global/v1/messaging/sign`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Accepts a message payload and signs it with a JWS (JSON Web Signature) using the a specific key from the DID (Decentralized Identifier) provided in the request.
### **Analytic events**
* MESSAGING_SIGN_START
* MESSAGING_SIGN_SUCCESS
* MESSAGING_SIGN_FAIL
### Request Body
Sign message request
```json
{
"didUrl": "did:example:abcdefghijkl#key1",
"payload": {
"msg": "this is a message"
}
}
```
### Responses
#### 200 - Message signed
```json
"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa21mazNtMldIQlVxVm94SlZ3R1NQejVrYmFKNnpBMXRwN1JRWUJiUUdtczNoI3o2TWttZmszbTJXSEJVcVZveEpWd0dTUHo1a2JhSjZ6QTF0cDdSUVlCYlFHbXMzaCJ9.eyJtc2ciOiJUaGlzIGlzIGEgcGF5bG9hZCJ9.5E9qEmmSOMHLABAr4A9VzuNKFaO4EDo2GSCMoxQm9zsE7eCmEEuaAxtNhOUdd-Wvj64vqBBVl84XB1Yg7X9wBg"
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Verify a message
## Endpoint
```
POST /v1/messaging/verify
```
Full URL: `https://example.vii.au01.mattr.global/v1/messaging/verify`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Verifies the signature of a provided JWS (JSON Web Signature), validating that the payload has not been tampered with and verifying that the kid in the JWS header is the same as the `iss` value in the Request Object.
One use case for verifying a JWS with a DID is when the Mobile Wallet App sends a Request Object to an OpenID Provider as part of the Authorization Code Flow (as per https://openid.net/specs/openid-connect-core-1_0-final.html#RequestObject). The Request Object is wrapped in a JWS with a signature that is generated from the Subject DID on the mobile app. Therefore verifying the JWS proves that the mobile app has access to the private key of the Subject DID.
### **Analytic events**
* MESSAGING_VERIFY_START
* MESSAGING_VERIFY_SUCCESS
* MESSAGING_VERIFY_FAIL
### Request Body
Provide the JWS to verify
```json
{
"jws": "EXAMPLE_JWS_TOKEN_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
}
```
### Responses
#### 200 - Verification successful
```json
{
"didUrl": "did:web:organization.com#2vcj3MjR4d",
"did": "did:web:organization.com",
"verified": true
}
```
#### 400 - Invalid JWS
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Encrypt a message
## Endpoint
```
POST /v1/messaging/encrypt
```
Full URL: `https://example.vii.au01.mattr.global/v1/messaging/encrypt`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Encrypts the provided payload using into a JWM (JSON Web Message) format.
### **Analytic events**
* MESSAGING_ENCRYPT_START
* MESSAGING_ENCRYPT_SUCCESS
* MESSAGING_ENCRYPT_FAIL
### Request Body
Encryption parameters
### Responses
#### 200 - Message encrypted
#### 400 - Bad Request
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Decrypt a message
## Endpoint
```
POST /v1/messaging/decrypt
```
Full URL: `https://example.vii.au01.mattr.global/v1/messaging/decrypt`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Decrypts a provided message where the tenant manages the keys for the defined `recipientDidUrl`.
### **Analytic events**
* MESSAGING_DECRYPT_START
* MESSAGING_DECRYPT_SUCCESS
* MESSAGING_DECRYPT_FAIL
### Request Body
Decryption parameters
### Responses
#### 200 - Message Decrypted
```json
{
"senderDidUrl": "did:web:organization.com#2vcj3MjR4d",
"recipientDidUrl": "did:key:z6MkgmEkNM32vyFeMXcQA7AfQDznu47qHCZpy2AYH2Dtdu1d"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Send a message
## Endpoint
```
POST /v1/messaging/send
```
Full URL: `https://example.vii.au01.mattr.global/v1/messaging/send`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Sends an encrypted JWM (JSON Web Messaging) format message to a service endpoint defined in a public DID document.
### **Analytic events**
* MESSAGING_SEND_START
* MESSAGING_SEND_SUCCESS
* MESSAGING_SEND_FAIL
### Request Body
### Responses
#### 200 - Message sent
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Search users
## Endpoint
```
POST /v1/users/search
```
Full URL: `https://example.vii.au01.mattr.global/v1/users/search`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns a list of users from the tenant that match all the provided search criteria (all criteria are optional).
### **Analytic events**
* USER_SEARCH_START
* USER_SEARCH_SUCCESS
* USER_SEARCH_FAIL
### Request Body
The search criteria
```json
{
"claims": {
"externalUserId": "0c3fad74-a8df-4a2d-8e75-f2d356b413ba"
},
"authenticationProvider": {
"providerId": "41458e5a-9092-40b7-9a26-d4eb43c5792f",
"subjectId": "example-university-oauth2|123456789"
},
"limit": 100,
"cursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1h"
}
```
### Responses
#### 200 - Users retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM",
"data": [
{
"id": "0c3fad74-a8df-4a2d-8e75-f2d356b413ba",
"claims": {
"externalUserId": "0c3fad74-a8df-4a2d-8e75-f2d356b413ba"
},
"authenticationProvider": {
"providerId": "41458e5a-9092-40b7-9a26-d4eb43c5792f",
"url": "https://example-university.au.auth0.com",
"subjectId": "example-university-oauth2|123456789"
}
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all users
## Endpoint
```
GET /v1/users
```
Full URL: `https://example.vii.au01.mattr.global/v1/users`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns a list of all users on your tenant.
### **Analytic events**
* USER_RETRIEVE_LIST_START
* USER_RETRIEVE_LIST_SUCCESS
* USER_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Users retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM",
"data": [
{
"id": "0c3fad74-a8df-4a2d-8e75-f2d356b413ba",
"claims": {
"externalUserId": "0c3fad74-a8df-4a2d-8e75-f2d356b413ba"
},
"authenticationProvider": {
"providerId": "41458e5a-9092-40b7-9a26-d4eb43c5792f",
"url": "https://example-university.au.auth0.com",
"subjectId": "example-university-oauth2|123456789"
}
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create a User
## Endpoint
```
POST /v1/users
```
Full URL: `https://example.vii.au01.mattr.global/v1/users`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Create a user.
* USER_CREATE_START
* USER_CREATE_SUCCESS
* USER_CREATE_FAIL
### Request Body
Create a User
```json
{
"claims": {
"externalUserId": "0c3fad74-a8df-4a2d-8e75-f2d356b413ba"
},
"authenticationProvider": {
"providerId": "41458e5a-9092-40b7-9a26-d4eb43c5792f",
"subjectId": "example-university-oauth2|123456789"
}
}
```
### Responses
#### 201 - User created
```json
{
"id": "0c3fad74-a8df-4a2d-8e75-f2d356b413ba",
"claims": {
"externalUserId": "0c3fad74-a8df-4a2d-8e75-f2d356b413ba"
},
"authenticationProvider": {
"providerId": "41458e5a-9092-40b7-9a26-d4eb43c5792f",
"url": "https://example-university.au.auth0.com",
"subjectId": "example-university-oauth2|123456789"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a user
## Endpoint
```
GET /v1/users/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v1/users/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieve an existing user by providing its ID.
* USER_RETRIEVE_START
* USER_RETRIEVE_SUCCESS
* USER_RETRIEVE_FAIL
### Responses
#### 200 - User retrieved
```json
{
"id": "0c3fad74-a8df-4a2d-8e75-f2d356b413ba",
"claims": {
"externalUserId": "0c3fad74-a8df-4a2d-8e75-f2d356b413ba"
},
"authenticationProvider": {
"providerId": "41458e5a-9092-40b7-9a26-d4eb43c5792f",
"url": "https://example-university.au.auth0.com",
"subjectId": "example-university-oauth2|123456789"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a user
## Endpoint
```
DELETE /v1/users/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v1/users/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Deletes a user and removes all data related to them by providing the user ID.
Deleting a user is irreversible. All of the user's data is removed, and any credential previously issued to them is no longer valid.
### **Analytic events**
* USER_DELETE_START
* USER_DELETE_SUCCESS
* USER_DELETE_FAIL
### Responses
#### 204 - User deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a User
## Endpoint
```
PUT /v1/users/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v1/users/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Updates an existing user by providing its ID.
* USER_UPDATE_START
* USER_UPDATE_SUCCESS
* USER_UPDATE_FAIL
### Request Body
Update a User
```json
{
"claims": {
"externalUserId": "0c3fad74-a8df-4a2d-8e75-f2d356b413ba"
},
"authenticationProvider": {
"providerId": "41458e5a-9092-40b7-9a26-d4eb43c5792f",
"subjectId": "example-university-oauth2|123456789"
}
}
```
### Responses
#### 200 - User updated
```json
{
"id": "0c3fad74-a8df-4a2d-8e75-f2d356b413ba",
"claims": {
"externalUserId": "0c3fad74-a8df-4a2d-8e75-f2d356b413ba"
},
"authenticationProvider": {
"providerId": "41458e5a-9092-40b7-9a26-d4eb43c5792f",
"url": "https://example-university.au.auth0.com",
"subjectId": "example-university-oauth2|123456789"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all user credentials data
## Endpoint
```
GET /v1/users/{userId}/credentials
```
Full URL: `https://example.vii.au01.mattr.global/v1/users/{userId}/credentials`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns metadata for all the credentials issued to the provided `userId`.
### **Analytic events**
* USER_CREDENTIAL_RETRIEVE_LIST_START
* USER_CREDENTIAL_RETRIEVE_LIST_SUCCESS
* USER_CREDENTIAL_RETRIEVE_LIST_FAIL
### Responses
#### 200 - User credentials retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all Authentication Providers
## Endpoint
```
GET /v1/users/authentication-providers
```
Full URL: `https://example.vii.au01.mattr.global/v1/users/authentication-providers`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Returns a list of all Authentication Providers on your tenant.
### **Analytic events**
* USER_AUTHENTICATION_PROVIDER_RETRIEVE_LIST_START
* USER_AUTHENTICATION_PROVIDER_RETRIEVE_LIST_SUCCESS
* USER_AUTHENTICATION_PROVIDER_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Authentication Providers retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM",
"data": [
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"redirectUrl": "https://tenant.vii.mattr.global/v1/oauth/authentication/callback",
"scope": [
"openid",
"profile",
"email",
"address",
"phone"
],
"clientId": "vJ0SCKchr4XjC0xHNE8DkH6Pmlg2lkCN",
"tokenEndpointAuthMethod": "client_secret_post",
"staticRequestParameters": {
"prompt": "login",
"max_age": 10000
},
"forwardedRequestParameters": [
"login_hint"
],
"claimsToPersist": [
"userId"
],
"clientSecret": "***********************************************************6-OjH"
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Configure an Authentication Provider
## Endpoint
```
POST /v1/users/authentication-providers
```
Full URL: `https://example.vii.au01.mattr.global/v1/users/authentication-providers`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Configures an Authentication Provider on the tenant.
An authentication or identity provider (IdP) is a platform that is typically used to store and manage user accounts on behalf of an organization or a service provider. MATTR VII uses the authentication provider to authenticate end users before issuing them credentials.
Only one authentication provider can be configured on a tenant.
The `/.well-known/openid-configuration` endpoint of the Authentication Provider must contain values for the `authorization_endpoint`, `token_endpoint` and `scopes_supported`.
### **Analytic events**
* USER_AUTHENTICATION_PROVIDER_CREATE_START
* USER_AUTHENTICATION_PROVIDER_CREATE_SUCCESS
* USER_AUTHENTICATION_PROVIDER_CREATE_FAIL
### Request Body
The Authentication Provider payload
```json
{
"scope": [
"openid",
"profile",
"email",
"address",
"phone"
],
"clientId": "vJ0SCKchr4XjC0xHNE8DkH6Pmlg2lkCN",
"tokenEndpointAuthMethod": "client_secret_post",
"staticRequestParameters": {
"prompt": "login",
"max_age": 10000
},
"forwardedRequestParameters": [
"login_hint"
],
"claimsToPersist": [
"userId"
],
"clientSecret": "QNwfa4Yi4Im9zy1u_15n7SzWKt-9G5cdH0r1bONRpUPfN-UIRaaXv_90z8V6-OjH",
"url": "https://example-university.au.auth0.com"
}
```
### Responses
#### 201 - Authentication Provider configured
```json
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"redirectUrl": "https://tenant.vii.mattr.global/v1/oauth/authentication/callback",
"scope": [
"openid",
"profile",
"email",
"address",
"phone"
],
"clientId": "vJ0SCKchr4XjC0xHNE8DkH6Pmlg2lkCN",
"tokenEndpointAuthMethod": "client_secret_post",
"staticRequestParameters": {
"prompt": "login",
"max_age": 10000
},
"forwardedRequestParameters": [
"login_hint"
],
"claimsToPersist": [
"userId"
],
"clientSecret": "***********************************************************6-OjH"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve an Authentication Provider
## Endpoint
```
GET /v1/users/authentication-providers/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v1/users/authentication-providers/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieve an existing Authentication Provider by providing its ID.
### **Analytic events**
* USER_AUTHENTICATION_PROVIDER_RETRIEVE_START
* USER_AUTHENTICATION_PROVIDER_RETRIEVE_SUCCESS
* USER_AUTHENTICATION_PROVIDER_RETRIEVE_FAIL
### Responses
#### 200 - Authentication Provider retrieved
```json
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"redirectUrl": "https://tenant.vii.mattr.global/v1/oauth/authentication/callback",
"scope": [
"openid",
"profile",
"email",
"address",
"phone"
],
"clientId": "vJ0SCKchr4XjC0xHNE8DkH6Pmlg2lkCN",
"tokenEndpointAuthMethod": "client_secret_post",
"staticRequestParameters": {
"prompt": "login",
"max_age": 10000
},
"forwardedRequestParameters": [
"login_hint"
],
"claimsToPersist": [
"userId"
],
"clientSecret": "***********************************************************6-OjH"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete an Authentication Provider
## Endpoint
```
DELETE /v1/users/authentication-providers/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v1/users/authentication-providers/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Deletes an existing Authentication Provider by providing its ID.
### **Analytic events**
* USER_AUTHENTICATION_PROVIDER_DELETE_START
* USER_AUTHENTICATION_PROVIDER_DELETE_SUCCESS
* USER_AUTHENTICATION_PROVIDER_DELETE_FAIL
### Responses
#### 204 - Authentication Provider deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update an Authentication Provider
## Endpoint
```
PUT /v1/users/authentication-providers/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v1/users/authentication-providers/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Updates an existing Authentication Provider by providing its ID.
### **Analytic events**
* USER_AUTHENTICATION_PROVIDER_UPDATE_START
* USER_AUTHENTICATION_PROVIDER_UPDATE_SUCCESS
* USER_AUTHENTICATION_PROVIDER_UPDATE_FAIL
### Request Body
Update an Authentication Provider
```json
{
"scope": [
"openid",
"profile",
"email",
"address",
"phone"
],
"clientId": "vJ0SCKchr4XjC0xHNE8DkH6Pmlg2lkCN",
"tokenEndpointAuthMethod": "client_secret_post",
"staticRequestParameters": {
"prompt": "login",
"max_age": 10000
},
"forwardedRequestParameters": [
"login_hint"
],
"claimsToPersist": [
"userId"
],
"clientSecret": "QNwfa4Yi4Im9zy1u_15n7SzWKt-9G5cdH0r1bONRpUPfN-UIRaaXv_90z8V6-OjH"
}
```
### Responses
#### 200 - Authentication Provider updated
```json
{
"id": "983c0a86-204f-4431-9371-f5a22e506599",
"redirectUrl": "https://tenant.vii.mattr.global/v1/oauth/authentication/callback",
"scope": [
"openid",
"profile",
"email",
"address",
"phone"
],
"clientId": "vJ0SCKchr4XjC0xHNE8DkH6Pmlg2lkCN",
"tokenEndpointAuthMethod": "client_secret_post",
"staticRequestParameters": {
"prompt": "login",
"max_age": 10000
},
"forwardedRequestParameters": [
"login_hint"
],
"claimsToPersist": [
"userId"
],
"clientSecret": "***********************************************************6-OjH"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve Interaction Hook
## Endpoint
```
GET /v1/openid/configuration
```
Full URL: `https://example.vii.au01.mattr.global/v1/openid/configuration`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves the Interaction Hook configuration from your tenant.
### **Analytic events**
* CREDENTIAL_PROVIDER_OPENID_CONFIGURATION_RETRIEVE_START
* CREDENTIAL_PROVIDER_OPENID_CONFIGURATION_RETRIEVE_SUCCESS
* CREDENTIAL_PROVIDER_OPENID_CONFIGURATION_RETRIEVE_FAIL
### Responses
#### 200 - Interaction Hook configuration retrieved
```json
{
"interactionHook": {
"url": "https://example-university.com/callback",
"claims": [
"first_name",
"last_name",
"email"
],
"sessionTimeoutInSec": 1200,
"disabled": false,
"secret": "dGtUrijBOT6UUJ8JO4kAFyGfhahDlVVeIk/sPbWTa7c="
}
}
```
# Configure Interaction Hook
## Endpoint
```
PUT /v1/openid/configuration
```
Full URL: `https://example.vii.au01.mattr.global/v1/openid/configuration`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Configure the Interaction Hook for the OID4VCI protocol on your tenant.
Many credential issuance journeys require the issuer to perform custom interactions with the user. This could be gathering more information, performing additional authentication steps (E.g, 2FA, MFA or biometric checks) or communicating the terms of service. To facilitate this requirement, you can configure MATTR VII to invoke an interaction hook which will redirect the user to a custom component during the credential issuance journey. This redirect happens **after** the user is authenticated with your configured identity provider but **before** the credential is issued to the user. Upon successful completion of the interaction hook, your custom component will redirect the user back to their digital wallet to complete the credential issuance flow. Your interaction hook component can be either a web or native application. We recommend using a web interface because it's more compatible with most scenarios.
You can only configure one interaction hook on your MATTR VII tenant. If you require several custom interactions as part of the credential issuance workflow, they should all be linked into a single interaction hook component.
### **Analytic events**
* CREDENTIAL_PROVIDER_OPENID_CONFIGURATION_UPDATE_START
* CREDENTIAL_PROVIDER_OPENID_CONFIGURATION_UPDATE_SUCCESS
* CREDENTIAL_PROVIDER_OPENID_CONFIGURATION_UPDATE_FAIL
### Request Body
The Interaction Hook configuration payload
```json
{
"interactionHook": {
"url": "https://example-university.com/callback",
"claims": [
"first_name",
"last_name",
"email"
],
"sessionTimeoutInSec": 1200,
"disabled": false
}
}
```
### Responses
#### 200 - Interaction Hook configured
```json
{
"interactionHook": {
"url": "https://example-university.com/callback",
"claims": [
"first_name",
"last_name",
"email"
],
"sessionTimeoutInSec": 1200,
"disabled": false,
"secret": "dGtUrijBOT6UUJ8JO4kAFyGfhahDlVVeIk/sPbWTa7c="
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve events
## Endpoint
```
GET /v1/events
```
Full URL: `https://example.vii.au01.mattr.global/v1/events`
### Authorization
Bearer token required.
Required roles: admin, auditor, managed-issuer
## Description
Returns a list of matching events from the tenant's event database.
The `categories` and `types` parameters filter based on an **OR** logic, whilst all other parameters use an "AND" logic. For example `(categories OR types) AND requestIds AND dateFrom`.
Refer to the [Events registry](https://api-reference-sdk.mattr.global/event-registry/latest/index.html) for an inclusive list of events categories and types.
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
- `ids`: array
Query by event IDs. These can be retrieved from event details.
- `requestIds`: array
Query by request IDs. These can be retrieved from event details. The response will include all the individual events that are part of the queried request.
- `categories`: array
Query by event categories. Uses an **OR** operation with `types`. Every **category** includes several event **types**. Each API endpoint details the event types it generates under the **Analytic events** heading. Refer to the [Events registry](https://api-reference-sdk.mattr.global/event-registry/latest/index.html) for an inclusive list.
- `types`: array
Query by event types. Uses an **OR** operation with `categories`. Every **category** includes several event **types**. Each API endpoint details the events it generates under the **Analytic events** heading. Refer to the [Events registry](https://api-reference-sdk.mattr.global/event-registry/latest/index.html) for an inclusive list.
- `dateFrom`: string
Query by event start date and time (inclusive), in ISO-8601 format.
- `dateTo`: string
Query by event end date and time (inclusive), in ISO-8601 format.
- `managementUserIds`
Filter events by management user IDs. You can obtain these IDs from the event details. The response will include all individual events associated with the specified management user IDs.
Special filtering values:
- `none`: Returns events that are not assigned to any management user IDs.
- `*`: Returns events that are assigned to any management user IDs.
- `clientIds`
Filter events by client IDs. You can obtain these IDs from the event details. The response will include all individual events associated with the specified client IDs.
Special filtering values:
- `none`: Returns events that are not assigned to any client IDs.
- `*`: Returns events that are assigned to any client IDs.
### Responses
#### 200 - A list of events
```json
{
"data": [
{
"id": "0c099611-19c4-4f29-8724-6b9e5ba1ef7c",
"type": "CREDENTIAL_COMPACT_SIGN_START",
"timestamp": "2023-06-01T02:45:44.087Z",
"category": "credential-compact",
"requestId": "4SO6JZz3sPYLjOQvxIVHr5",
"requestIp": "192.0.2.1",
"managementUserId": "ea691ed4-90ff-4be2-bd85-f2c74efa72c3",
"clientId": "54rp8Z8yGnlva19mThj7tJzNXFSyXrCf"
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 401 - Unauthorized. The client is not recognized by authorization server.
#### 403 - Forbidden. The client is recognized by authorization server but is not allowed to access this resource.
#### 500 - Internal Server Error. An unexpected error occurred.
# Create credential report
## Endpoint
```
POST /v1/events/credential-reports
```
Full URL: `https://example.vii.au01.mattr.global/v1/events/credential-reports`
### Authorization
Bearer token required.
Required roles: admin, issuer, auditor, managed-issuer
## Description
Creates a report of credential lifecycle operations (issuance and status changes).
Only CWT and mDoc credentials are included in the report.
### Request Body
The credential report parameters
```json
{
"timezoneOffset": "+02:00",
"startDate": "2026-02-01",
"endDate": "2026-02-28",
"profiles": [
"cwt",
"mdoc"
],
"limit": 100,
"cursor": "b2Zmc2V0PTM="
}
```
### Responses
#### 200 - Successfully generated credential report
```json
{
"data": [
{
"date": "2026-02-25",
"operation": "issued",
"profile": "mdoc",
"credentialType": "org.iso.18013.5.1.mDL",
"count": 5
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwMjYtMDItMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 401 - Unauthorized. The client is not recognized by authorization server.
#### 403 - Forbidden. The client is recognized by authorization server but is not allowed to access this resource.
#### 500 - Internal Server Error. An unexpected error occurred.
# Retrieve all ecosystems
## Endpoint
```
GET /v1/ecosystems
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieves a list of ecosystems.
### **Analytic events**
* ECOSYSTEM_RETRIEVE_LIST_START
* ECOSYSTEM_RETRIEVE_LIST_SUCCESS
* ECOSYSTEM_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Ecosystems retrieved
```json
{
"data": [
{
"id": "87880d7e-a4d0-462e-8383-3f1e5e16865d",
"name": "My Ecosystem"
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create ecosystem
## Endpoint
```
POST /v1/ecosystems
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Creates an ecosystem.
### **Analytic events**
* ECOSYSTEM_CREATE_START
* ECOSYSTEM_CREATE_SUCCESS
* ECOSYSTEM_CREATE_FAIL
### Request Body
```json
{
"name": "My Ecosystem"
}
```
### Responses
#### 201 - Ecosystem created
```json
{
"id": "87880d7e-a4d0-462e-8383-3f1e5e16865d",
"name": "My Ecosystem"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Sync ecosystem
## Endpoint
```
POST /v1/ecosystems/sync
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/sync`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Synchronizes all configured consumptions and integrations by retrieving the most recent trust information.
**Analytic events**
* ECOSYSTEM_TENANT_SYNC_ALL_START
* ECOSYSTEM_TENANT_SYNC_ALL_SUCCESS
* ECOSYSTEM_TENANT_SYNC_ALL_FAIL
### Responses
#### 202 - Ecosystem sync request accepted.
```json
{
"tenantConfiguration": {
"ecosystems": [
{
"url": "https://example.vii.au01.mattr.global/v1/ecosystems/489755c9-1d74-4f59-a127-db7105667bfe"
}
]
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve ecosystem
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieves an ecosystem by its ID.
### **Analytic events**
* ECOSYSTEM_RETRIEVE_START
* ECOSYSTEM_RETRIEVE_SUCCESS
* ECOSYSTEM_RETRIEVE_FAIL
### Responses
#### 200 - Ecosystem retrieved
```json
{
"id": "87880d7e-a4d0-462e-8383-3f1e5e16865d",
"name": "My Ecosystem"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete ecosystem
## Endpoint
```
DELETE /v1/ecosystems/{ecosystemId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Deletes an ecosystem by its ID.
### **Analytic events**
* ECOSYSTEM_DELETE_START
* ECOSYSTEM_DELETE_SUCCESS
* ECOSYSTEM_DELETE_FAIL
### Responses
#### 204 - Ecosystem deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Update ecosystem
## Endpoint
```
PUT /v1/ecosystems/{ecosystemId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Updates an ecosystem by its ID.
### **Analytic events**
* ECOSYSTEM_UPDATE_START
* ECOSYSTEM_UPDATE_SUCCESS
* ECOSYSTEM_UPDATE_FAIL
### Request Body
```json
{
"name": "My Ecosystem"
}
```
### Responses
#### 200 - Ecosystem updated
```json
{
"id": "87880d7e-a4d0-462e-8383-3f1e5e16865d",
"name": "My Ecosystem"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Retrieve ecosystem configuration
## Endpoint
```
GET /v1/config/ecosystems
```
Full URL: `https://example.vii.au01.mattr.global/v1/config/ecosystems`
### Authorization
Bearer token required.
Required roles: admin, dts-consumer
## Description
Retrieves the tenant's ecosystem configuration.
### **Analytic events**
* ECOSYSTEM_CONFIG_RETRIEVE_START
* ECOSYSTEM_CONFIG_RETRIEVE_SUCCESS
* ECOSYSTEM_CONFIG_RETRIEVE_FAIL
### Responses
#### 200 - Ecosystem configuration retrieved
```json
{
"ecosystems": [
{
"url": "https://example.vii.au01.mattr.global/v1/ecosystems/cdd42cec-e961-447c-9083-1312ee316053"
}
],
"isIssuanceRestricted": false,
"isVerificationRestricted": false
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Create ecosystem configuration
## Endpoint
```
POST /v1/config/ecosystems
```
Full URL: `https://example.vii.au01.mattr.global/v1/config/ecosystems`
### Authorization
Bearer token required.
Required roles: admin, dts-consumer
## Description
Creates an ecosystem configuration for the tenant.
### **Analytic events**
* ECOSYSTEM_CONFIG_CREATE_START
* ECOSYSTEM_CONFIG_CREATE_SUCCESS
* ECOSYSTEM_CONFIG_CREATE_FAIL
### Request Body
```json
{
"ecosystems": [
{
"url": "https://example.vii.au01.mattr.global/v1/ecosystems/cdd42cec-e961-447c-9083-1312ee316053"
}
],
"isIssuanceRestricted": false,
"isVerificationRestricted": false
}
```
### Responses
#### 201 - Ecosystem configuration created
```json
{
"ecosystems": [
{
"url": "https://example.vii.au01.mattr.global/v1/ecosystems/cdd42cec-e961-447c-9083-1312ee316053"
}
],
"isIssuanceRestricted": false,
"isVerificationRestricted": false
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Delete ecosystem configuration
## Endpoint
```
DELETE /v1/config/ecosystems
```
Full URL: `https://example.vii.au01.mattr.global/v1/config/ecosystems`
### Authorization
Bearer token required.
Required roles: admin, dts-consumer
## Description
Deletes the tenant's ecosystem configuration.
### **Analytic events**
* ECOSYSTEM_CONFIG_DELETE_START
* ECOSYSTEM_CONFIG_DELETE_SUCCESS
* ECOSYSTEM_CONFIG_DELETE_FAIL
### Responses
#### 204 - Ecosystem configuration deleted
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Update ecosystem configuration
## Endpoint
```
PUT /v1/config/ecosystems
```
Full URL: `https://example.vii.au01.mattr.global/v1/config/ecosystems`
### Authorization
Bearer token required.
Required roles: admin, dts-consumer
## Description
Updates the tenant's ecosystem configuration.
### **Analytic events**
* ECOSYSTEM_CONFIG_UPDATE_START
* ECOSYSTEM_CONFIG_UPDATE_SUCCESS
* ECOSYSTEM_CONFIG_UPDATE_FAIL
### Request Body
```json
{
"ecosystems": [
{
"url": "https://example.vii.au01.mattr.global/v1/ecosystems/cdd42cec-e961-447c-9083-1312ee316053"
}
],
"isIssuanceRestricted": false,
"isVerificationRestricted": false
}
```
### Responses
#### 200 - Ecosystem configuration updated
```json
{
"ecosystems": [
{
"url": "https://example.vii.au01.mattr.global/v1/ecosystems/cdd42cec-e961-447c-9083-1312ee316053"
}
],
"isIssuanceRestricted": false,
"isVerificationRestricted": false
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Publish policy
## Endpoint
```
POST /v1/ecosystems/{ecosystemId}/policies
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/policies`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Publish a new version of the ecosystem policy. Only active and currently valid participants and IACA certificates are included in the policy.
### **Analytic events**
* ECOSYSTEM_POLICY_CREATE_START
* ECOSYSTEM_POLICY_CREATE_SUCCESS
* ECOSYSTEM_POLICY_CREATE_FAIL
### Responses
#### 201 - Ecosystem policy published
```json
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"policyPublishedAt": "2024-10-22T00:00:00Z"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Retrieve latest ecosystem policy
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/policies/public/latest
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/policies/public/latest`
### Authorization
None required.
## Description
Retrieves the latest ecosystem policy by providing the ecosystem ID.
This endpoint is intended for public consumption, and as such does not require authentication.
### **Analytic events**
* ECOSYSTEM_POLICY_RETRIEVE_LATEST_START
* ECOSYSTEM_POLICY_RETRIEVE_LATEST_SUCCESS
* ECOSYSTEM_POLICY_RETRIEVE_LATEST_FAIL
### Responses
#### 200 - Latest ecosystem policy retrieved
```json
{
"policyModifiedAt": "2023-10-17T00:00:00Z",
"policyPublishedAt": "2024-10-22T00:00:00Z",
"credentials": {
"599bf148-d711-405a-a20b-9c8a87ac8850": {
"id": "599bf148-d711-405a-a20b-9c8a87ac8850",
"ecosystemId": "87880d7e-a4d0-462e-8383-3f1e5e16865d",
"type": "DriverLicense",
"name": "Driver's License"
}
},
"participants": {
"a24e391a-c27f-4b6e-9805-1ee7e03f3c58": {
"id": "a24e391a-c27f-4b6e-9805-1ee7e03f3c58",
"ecosystemId": "87880d7e-a4d0-462e-8383-3f1e5e16865d",
"name": "My Participant",
"identifiers": {
"web-semantic": "did:web:example.com",
"compact-semantic": "did:web:example.com",
"compact": "did:web:example.com",
"mobile": [
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"docTypes": [
"org.iso.18013.5.1"
]
}
]
},
"isIssuer": false,
"isVerifier": false,
"isIssuerConstrained": true,
"isVerifierConstrained": true,
"status": "Active",
"country": "US",
"stateOrProvince": "US-AL",
"organizationAddress": "1234 Main St, City, State, 12345",
"organizationPhoneNumber": "012-3456789",
"issuerAllowedCredentials": [
"e0a07846-44e1-41a4-b704-1ccf6eb1a5af",
"25fa6ffc-bf6e-417c-865c-96fcf1d7d1a3"
],
"verifierAllowedCredentials": [
"4e25a240-76bb-4e9f-9f93-b93be287922b",
"daca4a43-3ff9-4ecb-93fe-d9104e36bf74"
]
}
}
}
```
#### 304 - Not Modified. The resource has not been modified since the last request.
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve ecosystem policy
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/policy
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/policy`
### Authorization
None required.
## Description
Retrieves an ecosystem policy by providing the ecosystem ID.
This endpoint is intended for public consumption, and as such does not require authentication.
### **Analytic events**
* ECOSYSTEM_POLICY_RETRIEVE_LATEST_START
* ECOSYSTEM_POLICY_RETRIEVE_LATEST_SUCCESS
* ECOSYSTEM_POLICY_RETRIEVE_LATEST_FAIL
### Responses
#### 200 - Ecosystem policy retrieved
```json
{
"policyModifiedAt": "2023-10-17T00:00:00Z",
"policyPublishedAt": "2024-10-22T00:00:00Z",
"credentials": {
"599bf148-d711-405a-a20b-9c8a87ac8850": {
"id": "599bf148-d711-405a-a20b-9c8a87ac8850",
"ecosystemId": "87880d7e-a4d0-462e-8383-3f1e5e16865d",
"type": "DriverLicense",
"name": "Driver's License"
}
},
"participants": {
"a24e391a-c27f-4b6e-9805-1ee7e03f3c58": {
"id": "a24e391a-c27f-4b6e-9805-1ee7e03f3c58",
"ecosystemId": "87880d7e-a4d0-462e-8383-3f1e5e16865d",
"name": "My Participant",
"identifiers": {
"web-semantic": "did:web:example.com",
"compact-semantic": "did:web:example.com",
"compact": "did:web:example.com",
"mobile": [
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"docTypes": [
"org.iso.18013.5.1"
]
}
]
},
"isIssuer": false,
"isVerifier": false,
"isIssuerConstrained": true,
"isVerifierConstrained": true,
"status": "Active",
"country": "US",
"stateOrProvince": "US-AL",
"organizationAddress": "1234 Main St, City, State, 12345",
"organizationPhoneNumber": "012-3456789",
"issuerAllowedCredentials": [
"e0a07846-44e1-41a4-b704-1ccf6eb1a5af",
"25fa6ffc-bf6e-417c-865c-96fcf1d7d1a3"
],
"verifierAllowedCredentials": [
"4e25a240-76bb-4e9f-9f93-b93be287922b",
"daca4a43-3ff9-4ecb-93fe-d9104e36bf74"
]
}
}
}
```
#### 304 - Not Modified. The resource has not been modified since the last request.
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve ecosystem policy preview
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/preview
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/preview`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieves an ecosystem policy preview by providing the Ecosystem's ID. A policy preview includes all the participants and credential types created in the ecosystem, excluding any participants with expired or inactive IACAs.
### **Analytic events**
* ECOSYSTEM_POLICY_PREVIEW_RETRIEVE_START
* ECOSYSTEM_POLICY_PREVIEW_RETRIEVE_SUCCESS
* ECOSYSTEM_POLICY_PREVIEW_RETRIEVE_FAIL
### Responses
#### 200 - Ecosystem policy preview retrieved
```json
{
"policyModifiedAt": "2023-10-17T00:00:00Z",
"credentials": {
"599bf148-d711-405a-a20b-9c8a87ac8850": {
"id": "599bf148-d711-405a-a20b-9c8a87ac8850",
"ecosystemId": "87880d7e-a4d0-462e-8383-3f1e5e16865d",
"type": "DriverLicense",
"name": "Driver's License"
}
},
"participants": {
"a24e391a-c27f-4b6e-9805-1ee7e03f3c58": {
"id": "a24e391a-c27f-4b6e-9805-1ee7e03f3c58",
"ecosystemId": "87880d7e-a4d0-462e-8383-3f1e5e16865d",
"name": "My Participant",
"identifiers": {
"web-semantic": "did:web:example.com",
"compact-semantic": "did:web:example.com",
"compact": "did:web:example.com",
"mobile": [
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"status": "Active",
"docTypes": [
"org.iso.18013.5.1"
],
"validationResult": {
"validated": true
}
}
]
},
"isIssuer": false,
"isVerifier": false,
"isIssuerConstrained": true,
"isVerifierConstrained": true,
"status": "Active",
"country": "US",
"stateOrProvince": "US-AL",
"organizationAddress": "1234 Main St, City, State, 12345",
"organizationPhoneNumber": "012-3456789",
"issuerAllowedCredentials": [
"e0a07846-44e1-41a4-b704-1ccf6eb1a5af",
"25fa6ffc-bf6e-417c-865c-96fcf1d7d1a3"
],
"verifierAllowedCredentials": [
"4e25a240-76bb-4e9f-9f93-b93be287922b",
"daca4a43-3ff9-4ecb-93fe-d9104e36bf74"
]
}
}
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Publish issuer policy
## Endpoint
```
POST /v1/ecosystems/{ecosystemId}/issuer-policies
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/issuer-policies`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Publish a new version of the ecosystem issuer policy. A participant is included only if it is active and has at least one issuer certificate that passes validation. Only the certificates that pass validation are included.
### **Analytic events**
* ECOSYSTEM_ISSUER_POLICY_CREATE_START
* ECOSYSTEM_ISSUER_POLICY_CREATE_SUCCESS
* ECOSYSTEM_ISSUER_POLICY_CREATE_FAIL
### Responses
#### 201 - Issuer policy published
```json
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"publishedAt": "2024-10-22T00:00:00Z",
"participants": [
{
"id": "a24e391a-c27f-4b6e-9805-1ee7e03f3c58",
"name": "My Participant",
"issuerCertificates": [
{
"id": "599bf148-d711-405a-a20b-9c8a87ac8850",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\n-----END CERTIFICATE-----\r\n",
"certificateFingerprint": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
"docTypes": [
{
"value": "org.iso.18013.5.1.mDL"
}
]
}
]
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Retrieve issuer policy preview
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/issuer-policies/preview
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/issuer-policies/preview`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieves an ecosystem issuer policy preview by providing the ecosystem's ID. A policy preview includes all the participants and their issuer certificates created in the ecosystem, including those that fail validation and would be excluded from a published policy. The validation result is provided for each certificate.
### **Analytic events**
* ECOSYSTEM_ISSUER_POLICY_PREVIEW_RETRIEVE_START
* ECOSYSTEM_ISSUER_POLICY_PREVIEW_RETRIEVE_SUCCESS
* ECOSYSTEM_ISSUER_POLICY_PREVIEW_RETRIEVE_FAIL
### Responses
#### 200 - Issuer policy preview retrieved
```json
{
"publishedAt": "2024-10-22T00:00:00Z",
"participants": [
{
"id": "a24e391a-c27f-4b6e-9805-1ee7e03f3c58",
"name": "My Participant",
"status": "Active",
"issuerCertificates": [
{
"id": "599bf148-d711-405a-a20b-9c8a87ac8850",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\n-----END CERTIFICATE-----\r\n",
"certificateFingerprint": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
"docTypes": [
{
"value": "org.iso.18013.5.1.mDL"
}
],
"status": "Active"
}
],
"country": "NZ",
"stateOrProvince": "Wellington"
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve latest issuer policy
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/issuer-policies/public/latest
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/issuer-policies/public/latest`
### Authorization
None required.
## Description
Retrieves the latest published ecosystem issuer policy by providing the ecosystem ID.
This endpoint is intended for public consumption, and as such does not require authentication.
### **Analytic events**
* ECOSYSTEM_ISSUER_POLICY_RETRIEVE_LATEST_START
* ECOSYSTEM_ISSUER_POLICY_RETRIEVE_LATEST_SUCCESS
* ECOSYSTEM_ISSUER_POLICY_RETRIEVE_LATEST_FAIL
### Responses
#### 200 - Latest issuer policy retrieved
```json
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"publishedAt": "2024-10-22T00:00:00Z",
"participants": [
{
"id": "a24e391a-c27f-4b6e-9805-1ee7e03f3c58",
"name": "My Participant",
"issuerCertificates": [
{
"id": "599bf148-d711-405a-a20b-9c8a87ac8850",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\n-----END CERTIFICATE-----\r\n",
"certificateFingerprint": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
"docTypes": [
{
"value": "org.iso.18013.5.1.mDL"
}
]
}
]
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve issuer policy by ID
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/issuer-policies/public/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/issuer-policies/public/{id}`
### Authorization
None required.
## Description
Retrieves a specific published ecosystem issuer policy by providing the ecosystem ID and the issuer policy ID. The issuer policy ID is the `id` returned when a policy is published, and is also included in the response from the [Retrieve latest issuer policy](#operation/getLatestIssuerPolicy) endpoint.
This endpoint is intended for public consumption, and as such does not require authentication.
### **Analytic events**
* ECOSYSTEM_ISSUER_POLICY_RETRIEVE_START
* ECOSYSTEM_ISSUER_POLICY_RETRIEVE_SUCCESS
* ECOSYSTEM_ISSUER_POLICY_RETRIEVE_FAIL
### Responses
#### 200 - Issuer policy retrieved
```json
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"publishedAt": "2024-10-22T00:00:00Z",
"participants": [
{
"id": "a24e391a-c27f-4b6e-9805-1ee7e03f3c58",
"name": "My Participant",
"issuerCertificates": [
{
"id": "599bf148-d711-405a-a20b-9c8a87ac8850",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\n-----END CERTIFICATE-----\r\n",
"certificateFingerprint": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
"docTypes": [
{
"value": "org.iso.18013.5.1.mDL"
}
]
}
]
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete issuer policy
## Endpoint
```
DELETE /v1/ecosystems/{ecosystemId}/issuer-policies/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/issuer-policies/{id}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Deletes a published ecosystem issuer policy by providing the ecosystem ID and the issuer policy ID.
### Responses
#### 204 - Issuer policy deleted
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Publish verifier policy
## Endpoint
```
POST /v1/ecosystems/{ecosystemId}/verifier-policies
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/verifier-policies`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Publish a new version of the ecosystem verifier policy. A participant is included only if it is active and has at least one verifier certificate that passes validation. Only the certificates that pass validation are included.
### **Analytic events**
* ECOSYSTEM_VERIFIER_POLICY_CREATE_START
* ECOSYSTEM_VERIFIER_POLICY_CREATE_SUCCESS
* ECOSYSTEM_VERIFIER_POLICY_CREATE_FAIL
### Responses
#### 201 - Verifier policy published
```json
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"publishedAt": "2024-10-22T00:00:00Z",
"participants": [
{
"id": "a24e391a-c27f-4b6e-9805-1ee7e03f3c58",
"name": "My Participant",
"verifierCertificates": [
{
"id": "599bf148-d711-405a-a20b-9c8a87ac8850",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\n-----END CERTIFICATE-----\r\n",
"certificateFingerprint": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
}
]
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Retrieve verifier policy preview
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/verifier-policies/preview
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/verifier-policies/preview`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieves an ecosystem verifier policy preview by providing the ecosystem's ID. A policy preview includes all the participants and their verifier certificates created in the ecosystem, including those that fail validation and would be excluded from a published policy. The validation result is provided for each certificate.
### **Analytic events**
* ECOSYSTEM_VERIFIER_POLICY_PREVIEW_RETRIEVE_START
* ECOSYSTEM_VERIFIER_POLICY_PREVIEW_RETRIEVE_SUCCESS
* ECOSYSTEM_VERIFIER_POLICY_PREVIEW_RETRIEVE_FAIL
### Responses
#### 200 - Verifier policy preview retrieved
```json
{
"publishedAt": "2024-10-22T00:00:00Z",
"participants": [
{
"id": "a24e391a-c27f-4b6e-9805-1ee7e03f3c58",
"name": "My Participant",
"status": "Active",
"verifierCertificates": [
{
"id": "599bf148-d711-405a-a20b-9c8a87ac8850",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\n-----END CERTIFICATE-----\r\n",
"certificateFingerprint": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
"status": "Active"
}
],
"country": "NZ",
"stateOrProvince": "Wellington"
}
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve latest verifier policy
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/verifier-policies/public/latest
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/verifier-policies/public/latest`
### Authorization
None required.
## Description
Retrieves the latest published ecosystem verifier policy by providing the ecosystem ID.
This endpoint is intended for public consumption, and as such does not require authentication.
### **Analytic events**
* ECOSYSTEM_VERIFIER_POLICY_RETRIEVE_LATEST_START
* ECOSYSTEM_VERIFIER_POLICY_RETRIEVE_LATEST_SUCCESS
* ECOSYSTEM_VERIFIER_POLICY_RETRIEVE_LATEST_FAIL
### Responses
#### 200 - Latest verifier policy retrieved
```json
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"publishedAt": "2024-10-22T00:00:00Z",
"participants": [
{
"id": "a24e391a-c27f-4b6e-9805-1ee7e03f3c58",
"name": "My Participant",
"verifierCertificates": [
{
"id": "599bf148-d711-405a-a20b-9c8a87ac8850",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\n-----END CERTIFICATE-----\r\n",
"certificateFingerprint": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
}
]
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve verifier policy by ID
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/verifier-policies/public/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/verifier-policies/public/{id}`
### Authorization
None required.
## Description
Retrieves a specific published ecosystem verifier policy by providing the ecosystem ID and the verifier policy ID. The verifier policy ID is the `id` returned when a policy is published, and is also included in the response from the [Retrieve latest verifier policy](#operation/getLatestVerifierPolicy) endpoint.
This endpoint is intended for public consumption, and as such does not require authentication.
### **Analytic events**
* ECOSYSTEM_VERIFIER_POLICY_RETRIEVE_START
* ECOSYSTEM_VERIFIER_POLICY_RETRIEVE_SUCCESS
* ECOSYSTEM_VERIFIER_POLICY_RETRIEVE_FAIL
### Responses
#### 200 - Verifier policy retrieved
```json
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"publishedAt": "2024-10-22T00:00:00Z",
"participants": [
{
"id": "a24e391a-c27f-4b6e-9805-1ee7e03f3c58",
"name": "My Participant",
"verifierCertificates": [
{
"id": "599bf148-d711-405a-a20b-9c8a87ac8850",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\n-----END CERTIFICATE-----\r\n",
"certificateFingerprint": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
}
]
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve participants
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/participants
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieves a list of participants from the requested ecosystem.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_RETRIEVE_LIST_START
* ECOSYSTEM_PARTICIPANT_RETRIEVE_LIST_SUCCESS
* ECOSYSTEM_PARTICIPANT_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Participants retrieved
```json
{
"data": [
{
"id": "a24e391a-c27f-4b6e-9805-1ee7e03f3c58",
"ecosystemId": "87880d7e-a4d0-462e-8383-3f1e5e16865d",
"name": "My Participant",
"identifiers": {
"web-semantic": "did:web:example.com",
"compact-semantic": "did:web:example.com",
"compact": "did:web:example.com",
"mobile": [
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"status": "Active",
"docTypes": [
"org.iso.18013.5.1"
]
}
]
},
"isIssuer": false,
"isVerifier": false,
"isIssuerConstrained": true,
"isVerifierConstrained": true,
"status": "Active",
"country": "US",
"stateOrProvince": "US-AL",
"organizationAddress": "1234 Main St, City, State, 12345",
"organizationPhoneNumber": "012-3456789"
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create participant
## Endpoint
```
POST /v1/ecosystems/{ecosystemId}/participants
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Creates a participant in the requested ecosystem.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_CREATE_START
* ECOSYSTEM_PARTICIPANT_CREATE_SUCCESS
* ECOSYSTEM_PARTICIPANT_CREATE_FAIL
### Request Body
```json
{
"id": "a24e391a-c27f-4b6e-9805-1ee7e03f3c58",
"ecosystemId": "87880d7e-a4d0-462e-8383-3f1e5e16865d",
"name": "My Participant",
"identifiers": {
"web-semantic": "did:web:example.com",
"compact-semantic": "did:web:example.com",
"compact": "did:web:example.com",
"mobile": [
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"status": "Active",
"docTypes": [
"org.iso.18013.5.1"
]
}
]
},
"isIssuer": false,
"isVerifier": false,
"isIssuerConstrained": true,
"isVerifierConstrained": true,
"status": "Active",
"country": "US",
"stateOrProvince": "US-AL",
"organizationAddress": "1234 Main St, City, State, 12345",
"organizationPhoneNumber": "012-3456789"
}
```
### Responses
#### 201 - Participant created
```json
{
"id": "a24e391a-c27f-4b6e-9805-1ee7e03f3c58",
"ecosystemId": "87880d7e-a4d0-462e-8383-3f1e5e16865d",
"name": "My Participant",
"identifiers": {
"web-semantic": "did:web:example.com",
"compact-semantic": "did:web:example.com",
"compact": "did:web:example.com",
"mobile": [
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"status": "Active",
"docTypes": [
"org.iso.18013.5.1"
]
}
]
},
"isIssuer": false,
"isVerifier": false,
"isIssuerConstrained": true,
"isVerifierConstrained": true,
"status": "Active",
"country": "US",
"stateOrProvince": "US-AL",
"organizationAddress": "1234 Main St, City, State, 12345",
"organizationPhoneNumber": "012-3456789"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Retrieve participant
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/participants/{participantId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieves a participant from the requested ecosystem by its ID.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_RETRIEVE_START
* ECOSYSTEM_PARTICIPANT_RETRIEVE_SUCCESS
* ECOSYSTEM_PARTICIPANT_RETRIEVE_FAIL
### Responses
#### 200 - Participant retrieved
```json
{
"id": "a24e391a-c27f-4b6e-9805-1ee7e03f3c58",
"ecosystemId": "87880d7e-a4d0-462e-8383-3f1e5e16865d",
"name": "My Participant",
"identifiers": {
"web-semantic": "did:web:example.com",
"compact-semantic": "did:web:example.com",
"compact": "did:web:example.com",
"mobile": [
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"status": "Active",
"docTypes": [
"org.iso.18013.5.1"
]
}
]
},
"isIssuer": false,
"isVerifier": false,
"isIssuerConstrained": true,
"isVerifierConstrained": true,
"status": "Active",
"country": "US",
"stateOrProvince": "US-AL",
"organizationAddress": "1234 Main St, City, State, 12345",
"organizationPhoneNumber": "012-3456789"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete participant
## Endpoint
```
DELETE /v1/ecosystems/{ecosystemId}/participants/{participantId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Deletes a participant in the requested ecosystem by its ID.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_DELETE_START
* ECOSYSTEM_PARTICIPANT_DELETE_SUCCESS
* ECOSYSTEM_PARTICIPANT_DELETE_FAIL
### Responses
#### 204 - Participant deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update participant
## Endpoint
```
PUT /v1/ecosystems/{ecosystemId}/participants/{participantId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Updates a participant in the requested ecosystem by its ID.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_UPDATE_START
* ECOSYSTEM_PARTICIPANT_UPDATE_SUCCESS
* ECOSYSTEM_PARTICIPANT_UPDATE_FAIL
### Request Body
```json
{
"id": "a24e391a-c27f-4b6e-9805-1ee7e03f3c58",
"ecosystemId": "87880d7e-a4d0-462e-8383-3f1e5e16865d",
"name": "My Participant",
"identifiers": {
"web-semantic": "did:web:example.com",
"compact-semantic": "did:web:example.com",
"compact": "did:web:example.com",
"mobile": [
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"status": "Active",
"docTypes": [
"org.iso.18013.5.1"
]
}
]
},
"isIssuer": false,
"isVerifier": false,
"isIssuerConstrained": true,
"isVerifierConstrained": true,
"status": "Active",
"country": "US",
"stateOrProvince": "US-AL",
"organizationAddress": "1234 Main St, City, State, 12345",
"organizationPhoneNumber": "012-3456789"
}
```
### Responses
#### 200 - Participant updated
```json
{
"id": "a24e391a-c27f-4b6e-9805-1ee7e03f3c58",
"ecosystemId": "87880d7e-a4d0-462e-8383-3f1e5e16865d",
"name": "My Participant",
"identifiers": {
"web-semantic": "did:web:example.com",
"compact-semantic": "did:web:example.com",
"compact": "did:web:example.com",
"mobile": [
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"status": "Active",
"docTypes": [
"org.iso.18013.5.1"
]
}
]
},
"isIssuer": false,
"isVerifier": false,
"isIssuerConstrained": true,
"isVerifierConstrained": true,
"status": "Active",
"country": "US",
"stateOrProvince": "US-AL",
"organizationAddress": "1234 Main St, City, State, 12345",
"organizationPhoneNumber": "012-3456789"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Retrieve all participant points of contact
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/participants/{participantId}/contacts
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/contacts`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieve all points of contact for given participant.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_CONTACT_RETRIEVE_LIST_START
* ECOSYSTEM_PARTICIPANT_CONTACT_RETRIEVE_LIST_SUCCESS
* ECOSYSTEM_PARTICIPANT_CONTACT_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Participant points of contact retrieved
```json
{
"data": [
{
"id": "920f2489-d953-42f2-b2dd-f37c29b818cf",
"fullName": "John Doe",
"active": true,
"emailAddress": "john.doe@example.com",
"primaryPhoneNumber": 1234567890,
"secondaryPhoneNumber": 987654321,
"createdBy": {
"clientId": "f7b9ecfc-8431-470e-a1de-94b68dd92f68",
"managementUserId": "ab16dc65-b58e-4be2-ba77-a6f35021cb2b"
},
"createdAt": "2025-07-01T00:00:00.000Z",
"lastModifiedBy": {
"clientId": "f7b9ecfc-8431-470e-a1de-94b68dd92f68",
"managementUserId": "ab16dc65-b58e-4be2-ba77-a6f35021cb2b"
},
"lastModifiedAt": "2025-07-01T00:00:00.000Z"
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create a participant point of contact
## Endpoint
```
POST /v1/ecosystems/{ecosystemId}/participants/{participantId}/contacts
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/contacts`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Create a new point of contact for the specified participant. A maximum of 10 contacts can exist per participant.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_CONTACT_CREATE_START
* ECOSYSTEM_PARTICIPANT_CONTACT_CREATE_SUCCESS
* ECOSYSTEM_PARTICIPANT_CONTACT_CREATE_FAIL
### Request Body
```json
{
"fullName": "John Doe",
"active": true,
"emailAddress": "john.doe@example.com",
"primaryPhoneNumber": 1234567890,
"secondaryPhoneNumber": 987654321
}
```
### Responses
#### 201 - Participant point of contact created
```json
{
"id": "920f2489-d953-42f2-b2dd-f37c29b818cf",
"fullName": "John Doe",
"active": true,
"emailAddress": "john.doe@example.com",
"primaryPhoneNumber": 1234567890,
"secondaryPhoneNumber": 987654321,
"createdBy": {
"clientId": "f7b9ecfc-8431-470e-a1de-94b68dd92f68",
"managementUserId": "ab16dc65-b58e-4be2-ba77-a6f35021cb2b"
},
"createdAt": "2025-07-01T00:00:00.000Z",
"lastModifiedBy": {
"clientId": "f7b9ecfc-8431-470e-a1de-94b68dd92f68",
"managementUserId": "ab16dc65-b58e-4be2-ba77-a6f35021cb2b"
},
"lastModifiedAt": "2025-07-01T00:00:00.000Z"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a participant point of contact
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/participants/{participantId}/contacts/{contactId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/contacts/{contactId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieve a specific point of contact for a given participant.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_CONTACT_RETRIEVE_START
* ECOSYSTEM_PARTICIPANT_CONTACT_RETRIEVE_SUCCESS
* ECOSYSTEM_PARTICIPANT_CONTACT_RETRIEVE_FAIL
### Responses
#### 200 - Participant point of contact retrieved
```json
{
"id": "920f2489-d953-42f2-b2dd-f37c29b818cf",
"fullName": "John Doe",
"active": true,
"emailAddress": "john.doe@example.com",
"primaryPhoneNumber": 1234567890,
"secondaryPhoneNumber": 987654321,
"createdBy": {
"clientId": "f7b9ecfc-8431-470e-a1de-94b68dd92f68",
"managementUserId": "ab16dc65-b58e-4be2-ba77-a6f35021cb2b"
},
"createdAt": "2025-07-01T00:00:00.000Z",
"lastModifiedBy": {
"clientId": "f7b9ecfc-8431-470e-a1de-94b68dd92f68",
"managementUserId": "ab16dc65-b58e-4be2-ba77-a6f35021cb2b"
},
"lastModifiedAt": "2025-07-01T00:00:00.000Z"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a participant point of contact
## Endpoint
```
DELETE /v1/ecosystems/{ecosystemId}/participants/{participantId}/contacts/{contactId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/contacts/{contactId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Delete a specific point of contact for a given participant.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_CONTACT_DELETE_START
* ECOSYSTEM_PARTICIPANT_CONTACT_DELETE_SUCCESS
* ECOSYSTEM_PARTICIPANT_CONTACT_DELETE_FAIL
### Responses
#### 204 - Participant point of contact deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a participant point of contact
## Endpoint
```
PUT /v1/ecosystems/{ecosystemId}/participants/{participantId}/contacts/{contactId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/contacts/{contactId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Update a specific point of contact for a given participant.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_CONTACT_UPDATE_START
* ECOSYSTEM_PARTICIPANT_CONTACT_UPDATE_SUCCESS
* ECOSYSTEM_PARTICIPANT_CONTACT_UPDATE_FAIL
### Request Body
```json
{
"fullName": "John Doe",
"active": true,
"emailAddress": "john.doe@example.com",
"primaryPhoneNumber": 1234567890,
"secondaryPhoneNumber": 987654321
}
```
### Responses
#### 200 - Participant point of contact updated
```json
{
"id": "920f2489-d953-42f2-b2dd-f37c29b818cf",
"fullName": "John Doe",
"active": true,
"emailAddress": "john.doe@example.com",
"primaryPhoneNumber": 1234567890,
"secondaryPhoneNumber": 987654321,
"createdBy": {
"clientId": "f7b9ecfc-8431-470e-a1de-94b68dd92f68",
"managementUserId": "ab16dc65-b58e-4be2-ba77-a6f35021cb2b"
},
"createdAt": "2025-07-01T00:00:00.000Z",
"lastModifiedBy": {
"clientId": "f7b9ecfc-8431-470e-a1de-94b68dd92f68",
"managementUserId": "ab16dc65-b58e-4be2-ba77-a6f35021cb2b"
},
"lastModifiedAt": "2025-07-01T00:00:00.000Z"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all PDF evidence metadata
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/participants/{participantId}/evidence
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/evidence`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieve metadata for all PDF evidence uploaded for given participant.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_EVIDENCE_RETRIEVE_LIST_START
* ECOSYSTEM_PARTICIPANT_EVIDENCE_RETRIEVE_LIST_SUCCESS
* ECOSYSTEM_PARTICIPANT_EVIDENCE_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - PDF evidence metadata retrieved
```json
{
"data": [
{
"filename": "pdf-evidence.pdf",
"fileDescription": "This is an example file description",
"id": "920f2489-d953-42f2-b2dd-f37c29b818cf",
"uploadedAt": "2025-07-01T00:00:00.000Z"
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Upload PDF evidence
## Endpoint
```
POST /v1/ecosystems/{ecosystemId}/participants/{participantId}/evidence
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/evidence`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Upload a PDF file as evidence for the given participant. Maximum file size is 10MB.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_EVIDENCE_CREATE_START
* ECOSYSTEM_PARTICIPANT_EVIDENCE_CREATE_SUCCESS
* ECOSYSTEM_PARTICIPANT_EVIDENCE_CREATE_FAIL
### Request Body
### Responses
#### 201 - Evidence PDF uploaded
```json
{
"filename": "pdf-evidence.pdf",
"fileDescription": "This is an example file description",
"id": "920f2489-d953-42f2-b2dd-f37c29b818cf",
"uploadedAt": "2025-07-01T00:00:00.000Z"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a PDF evidence metadata
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/participants/{participantId}/evidence/{evidenceId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/evidence/{evidenceId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieve metadata for a given uploaded PDF evidence.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_EVIDENCE_RETRIEVE_START
* ECOSYSTEM_PARTICIPANT_EVIDENCE_RETRIEVE_SUCCESS
* ECOSYSTEM_PARTICIPANT_EVIDENCE_RETRIEVE_FAIL
### Responses
#### 200 - Evidence retrieved
```json
{
"filename": "pdf-evidence.pdf",
"fileDescription": "This is an example file description",
"id": "920f2489-d953-42f2-b2dd-f37c29b818cf",
"uploadedAt": "2025-07-01T00:00:00.000Z"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete PDF evidence
## Endpoint
```
DELETE /v1/ecosystems/{ecosystemId}/participants/{participantId}/evidence/{evidenceId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/evidence/{evidenceId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Delete metadata for a given uploaded PDF evidence and remove it from storage.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_EVIDENCE_DELETE_START
* ECOSYSTEM_PARTICIPANT_EVIDENCE_DELETE_SUCCESS
* ECOSYSTEM_PARTICIPANT_EVIDENCE_DELETE_FAIL
### Responses
#### 204 - PDF Evidence deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update PDF evidence metadata
## Endpoint
```
PUT /v1/ecosystems/{ecosystemId}/participants/{participantId}/evidence/{evidenceId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/evidence/{evidenceId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Update metadata for a given uploaded PDF evidence.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_EVIDENCE_UPDATE_START
* ECOSYSTEM_PARTICIPANT_EVIDENCE_UPDATE_SUCCESS
* ECOSYSTEM_PARTICIPANT_EVIDENCE_UPDATE_FAIL
### Request Body
### Responses
#### 200 - Evidence PDF metadata updated
```json
{
"filename": "pdf-evidence.pdf",
"fileDescription": "This is an example file description",
"id": "920f2489-d953-42f2-b2dd-f37c29b818cf",
"uploadedAt": "2025-07-01T00:00:00.000Z"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Download PDF evidence
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/participants/{participantId}/evidence/{evidenceId}/download
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/evidence/{evidenceId}/download`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieves a specific PDF evidence file.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_EVIDENCE_DOWNLOAD_START
* ECOSYSTEM_PARTICIPANT_EVIDENCE_DOWNLOAD_SUCCESS
* ECOSYSTEM_PARTICIPANT_EVIDENCE_DOWNLOAD_FAIL
### Responses
#### 200 - PDF Evidence file retrieved
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve credential types
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/credentials
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/credentials`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieves a list of credential types from the requested ecosystem.
### **Analytic events**
* ECOSYSTEM_CREDENTIAL_RETRIEVE_LIST_START
* ECOSYSTEM_CREDENTIAL_RETRIEVE_LIST_SUCCESS
* ECOSYSTEM_CREDENTIAL_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Credential types retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create credential type
## Endpoint
```
POST /v1/ecosystems/{ecosystemId}/credentials
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/credentials`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Creates a new credential type in the requested ecosystem.
### **Analytic events**
* ECOSYSTEM_CREDENTIAL_CREATE_START
* ECOSYSTEM_CREDENTIAL_CREATE_SUCCESS
* ECOSYSTEM_CREDENTIAL_CREATE_FAIL
### Request Body
### Responses
#### 201 - Credential type created
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Retrieve credential type
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/credentials/{credentialId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/credentials/{credentialId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieves a credential type from the requested ecosystem by its ID.
### **Analytic events**
* ECOSYSTEM_CREDENTIAL_RETRIEVE_START
* ECOSYSTEM_CREDENTIAL_RETRIEVE_SUCCESS
* ECOSYSTEM_CREDENTIAL_RETRIEVE_FAIL
### Responses
#### 200 - Credential type retrieved
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete credential type
## Endpoint
```
DELETE /v1/ecosystems/{ecosystemId}/credentials/{credentialId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/credentials/{credentialId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Deletes a credential type from the requested ecosystem by its ID.
### **Analytic events**
* ECOSYSTEM_CREDENTIAL_DELETE_START
* ECOSYSTEM_CREDENTIAL_DELETE_SUCCESS
* ECOSYSTEM_CREDENTIAL_DELETE_FAIL
### Responses
#### 204 - Credential type deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create issuer assignment
## Endpoint
```
POST /v1/ecosystems/{ecosystemId}/participants/{participantId}/issuer/credentials
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/issuer/credentials`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Assigns a credential type to the requested participant in the requested ecosystem.
### **Analytic events**
* ECOSYSTEM_ISSUER_POLICY_CREATE_START
* ECOSYSTEM_ISSUER_POLICY_CREATE_SUCCESS
* ECOSYSTEM_ISSUER_POLICY_CREATE_FAIL
### Request Body
```json
{
"credentialId": "599bf148-d711-405a-a20b-9c8a87ac8850"
}
```
### Responses
#### 201 - Issuer policy created
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Delete issuer assignment
## Endpoint
```
DELETE /v1/ecosystems/{ecosystemId}/participants/{participantId}/issuer/credentials/{credentialId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/issuer/credentials/{credentialId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Removes a credential type to the requested participant in the requested ecosystem.
### **Analytic events**
* ECOSYSTEM_ISSUER_POLICY_DELETE_START
* ECOSYSTEM_ISSUER_POLICY_DELETE_SUCCESS
* ECOSYSTEM_ISSUER_POLICY_DELETE_FAIL
### Responses
#### 204 - Issuer policy deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Create verifier assignment
## Endpoint
```
POST /v1/ecosystems/{ecosystemId}/participants/{participantId}/verifier/credentials
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/verifier/credentials`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Assigns a credential type to the requested participant in the requested ecosystem.
### **Analytic events**
* ECOSYSTEM_VERIFIER_POLICY_CREATE_START
* ECOSYSTEM_VERIFIER_POLICY_CREATE_SUCCESS
* ECOSYSTEM_VERIFIER_POLICY_CREATE_FAIL
### Request Body
```json
{
"credentialId": "599bf148-d711-405a-a20b-9c8a87ac8850"
}
```
### Responses
#### 201 - Verifier policy created
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Delete verifier assignment
## Endpoint
```
DELETE /v1/ecosystems/{ecosystemId}/participants/{participantId}/verifier/credentials/{credentialId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/verifier/credentials/{credentialId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Removes a credential type from the requested participant in the requested ecosystem..
### **Analytic events**
* ECOSYSTEM_VERIFIER_POLICY_DELETE_START
* ECOSYSTEM_VERIFIER_POLICY_DELETE_SUCCESS
* ECOSYSTEM_VERIFIER_POLICY_DELETE_FAIL
### Responses
#### 204 - Verifier policy deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Retrieve all participant issuer certificates
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/participants/{participantId}/issuer-certificates
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/issuer-certificates`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieve all issuer certificates for the specified participant.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_ISSUER_CERTIFICATE_RETRIEVE_LIST_START
* ECOSYSTEM_PARTICIPANT_ISSUER_CERTIFICATE_RETRIEVE_LIST_SUCCESS
* ECOSYSTEM_PARTICIPANT_ISSUER_CERTIFICATE_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Participant issuer certificates retrieved
```json
{
"data": [
{
"id": "3b5b1a6a-1b1a-4b1a-8b1a-1b1a4b1a8b1a",
"certificateFingerprint": "57B178A6C2B8C1877DBA515AD4FD60F9C805EFC309287182DB7DEBFE43A22928",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"status": "Inactive",
"docTypes": [
{
"value": "org.iso.18013.5.1"
}
],
"linkCertificateInfo": {
"issuerCertificateId": "3b5b1a6a-1b1a-4b1a-8b1a-1b1a4b1a8b1a",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"certificateFingerprint": "57B178A6C2B8C1877DBA515AD4FD60F9C805EFC309287182DB7DEBFE43A22928"
}
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create a participant issuer certificate
## Endpoint
```
POST /v1/ecosystems/{ecosystemId}/participants/{participantId}/issuer-certificates
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/issuer-certificates`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Create a new issuer certificate for the specified participant.
The certificatePem field cannot be modified once the certificate is created.
To maintain trust continuity across a certificate rotation, provide `linkCertificateInfo`
to add this certificate as a successor to a previously uploaded certificate. Link
certificates can only be added when the certificate is created, not when it is updated.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_ISSUER_CERTIFICATE_CREATE_START
* ECOSYSTEM_PARTICIPANT_ISSUER_CERTIFICATE_CREATE_SUCCESS
* ECOSYSTEM_PARTICIPANT_ISSUER_CERTIFICATE_CREATE_FAIL
### Request Body
```json
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"status": "Inactive",
"docTypes": [
{
"value": "org.iso.18013.5.1"
}
],
"linkCertificateInfo": {
"issuerCertificateId": "3b5b1a6a-1b1a-4b1a-8b1a-1b1a4b1a8b1a",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n"
}
}
```
### Responses
#### 201 - Participant issuer certificate created
```json
{
"id": "3b5b1a6a-1b1a-4b1a-8b1a-1b1a4b1a8b1a",
"certificateFingerprint": "57B178A6C2B8C1877DBA515AD4FD60F9C805EFC309287182DB7DEBFE43A22928",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"status": "Inactive",
"docTypes": [
{
"value": "org.iso.18013.5.1"
}
],
"linkCertificateInfo": {
"issuerCertificateId": "3b5b1a6a-1b1a-4b1a-8b1a-1b1a4b1a8b1a",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"certificateFingerprint": "57B178A6C2B8C1877DBA515AD4FD60F9C805EFC309287182DB7DEBFE43A22928"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a participant issuer certificate
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/participants/{participantId}/issuer-certificates/{issuerCertificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/issuer-certificates/{issuerCertificateId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieve a specific issuer certificate for the specified participant.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_ISSUER_CERTIFICATE_RETRIEVE_START
* ECOSYSTEM_PARTICIPANT_ISSUER_CERTIFICATE_RETRIEVE_SUCCESS
* ECOSYSTEM_PARTICIPANT_ISSUER_CERTIFICATE_RETRIEVE_FAIL
### Responses
#### 200 - Participant issuer certificate retrieved
```json
{
"id": "3b5b1a6a-1b1a-4b1a-8b1a-1b1a4b1a8b1a",
"certificateFingerprint": "57B178A6C2B8C1877DBA515AD4FD60F9C805EFC309287182DB7DEBFE43A22928",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"status": "Inactive",
"docTypes": [
{
"value": "org.iso.18013.5.1"
}
],
"linkCertificateInfo": {
"issuerCertificateId": "3b5b1a6a-1b1a-4b1a-8b1a-1b1a4b1a8b1a",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"certificateFingerprint": "57B178A6C2B8C1877DBA515AD4FD60F9C805EFC309287182DB7DEBFE43A22928"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a participant issuer certificate
## Endpoint
```
DELETE /v1/ecosystems/{ecosystemId}/participants/{participantId}/issuer-certificates/{issuerCertificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/issuer-certificates/{issuerCertificateId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Delete a specific issuer certificate for the specified participant.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_ISSUER_CERTIFICATE_DELETE_START
* ECOSYSTEM_PARTICIPANT_ISSUER_CERTIFICATE_DELETE_SUCCESS
* ECOSYSTEM_PARTICIPANT_ISSUER_CERTIFICATE_DELETE_FAIL
### Responses
#### 204 - Participant issuer certificate deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a participant issuer certificate
## Endpoint
```
PUT /v1/ecosystems/{ecosystemId}/participants/{participantId}/issuer-certificates/{issuerCertificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/issuer-certificates/{issuerCertificateId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Update a specific issuer certificate for the specified participant.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_ISSUER_CERTIFICATE_UPDATE_START
* ECOSYSTEM_PARTICIPANT_ISSUER_CERTIFICATE_UPDATE_SUCCESS
* ECOSYSTEM_PARTICIPANT_ISSUER_CERTIFICATE_UPDATE_FAIL
### Request Body
```json
{
"status": "Active",
"docTypes": [
{
"value": "org.iso.18013.5.1"
}
]
}
```
### Responses
#### 200 - Participant issuer certificate updated
```json
{
"id": "3b5b1a6a-1b1a-4b1a-8b1a-1b1a4b1a8b1a",
"certificateFingerprint": "57B178A6C2B8C1877DBA515AD4FD60F9C805EFC309287182DB7DEBFE43A22928",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"status": "Inactive",
"docTypes": [
{
"value": "org.iso.18013.5.1"
}
],
"linkCertificateInfo": {
"issuerCertificateId": "3b5b1a6a-1b1a-4b1a-8b1a-1b1a4b1a8b1a",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"certificateFingerprint": "57B178A6C2B8C1877DBA515AD4FD60F9C805EFC309287182DB7DEBFE43A22928"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all participant verifier certificates
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/participants/{participantId}/verifier-certificates
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/verifier-certificates`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieve all verifier certificates for the specified participant.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_VERIFIER_CERTIFICATE_RETRIEVE_LIST_START
* ECOSYSTEM_PARTICIPANT_VERIFIER_CERTIFICATE_RETRIEVE_LIST_SUCCESS
* ECOSYSTEM_PARTICIPANT_VERIFIER_CERTIFICATE_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Participant verifier certificates retrieved
```json
{
"data": [
{
"id": "3b5b1a6a-1b1a-4b1a-8b1a-1b1a4b1a8b1a",
"certificateFingerprint": "65A98EFAB9A22A1829391B22B668F9F1B758094C1A19D599B516BBF63CBF0015",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICTDCCAfKgAwIBAgIKeKcjIBGvXfS/sjAKBggqhkjOPQQDAjAwMQswCQYDVQQG\r\nEwJVUzEhMB8GA1UEAwwYRXhhbXBsZSBWZXJpZmllciByb290IENBMB4XDTI1MDgy\r\nNzIxMzMxNFoXDTMwMDgyNjIxMzMxNFowMDELMAkGA1UEBhMCVVMxITAfBgNVBAMM\r\nGEV4YW1wbGUgVmVyaWZpZXIgcm9vdCBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEH\r\nA0IABNJHM5ZE+fpVn7b9WwjVBiOiZq9eNXq1JkNj/6ZLe+2GkaRY/WE2Xbg7yx++\r\nh3QEdX3sGKzGO7dygQALBe/4qEyjgfMwgfAwHQYDVR0OBBYEFK8ogqdUH2vZlC1y\r\nNf619a8fnx8KMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMHsG\r\nA1UdHwR0MHIwcKBuoGyGamh0dHBzOi8vbGVhcm4udmlpLmF1MDEubWF0dHIuZ2xv\r\nYmFsL3YyL3ByZXNlbnRhdGlvbnMvY2VydGlmaWNhdGVzL2Q3YzE3ODI4LThkMTgt\r\nNDYyZS1iNDk3LWNjNjI2NWM4ZmQxYi9jcmwwLgYDVR0SBCcwJYYjaHR0cHM6Ly9s\r\nZWFybi52aWkuYXUwMS5tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSAAwRQIhAOqD\r\n0DF3rohBitl5jAj6x1164uGGj6yAhF/eE4aJeGc+AiAgaUYHzobzaPEWd+jZOh/A\r\nq8WgVJ+8sLx9WdJDs9/shQ==\r\n-----END CERTIFICATE-----\r\n",
"status": "Inactive",
"linkCertificateInfo": {
"issuerCertificateId": "3b5b1a6a-1b1a-4b1a-8b1a-1b1a4b1a8b1a",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"certificateFingerprint": "57B178A6C2B8C1877DBA515AD4FD60F9C805EFC309287182DB7DEBFE43A22928"
}
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create a participant verifier certificate
## Endpoint
```
POST /v1/ecosystems/{ecosystemId}/participants/{participantId}/verifier-certificates
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/verifier-certificates`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Create a new verifier certificate for the specified participant.
The certificatePem field cannot be modified once the certificate is created.
To maintain trust continuity across a certificate rotation, provide `linkCertificateInfo`
to add this certificate as a successor to a previously uploaded certificate. Link
certificates can only be added when the certificate is created, not when it is updated.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_VERIFIER_CERTIFICATE_CREATE_START
* ECOSYSTEM_PARTICIPANT_VERIFIER_CERTIFICATE_CREATE_SUCCESS
* ECOSYSTEM_PARTICIPANT_VERIFIER_CERTIFICATE_CREATE_FAIL
### Request Body
```json
{
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICTDCCAfKgAwIBAgIKeKcjIBGvXfS/sjAKBggqhkjOPQQDAjAwMQswCQYDVQQG\r\nEwJVUzEhMB8GA1UEAwwYRXhhbXBsZSBWZXJpZmllciByb290IENBMB4XDTI1MDgy\r\nNzIxMzMxNFoXDTMwMDgyNjIxMzMxNFowMDELMAkGA1UEBhMCVVMxITAfBgNVBAMM\r\nGEV4YW1wbGUgVmVyaWZpZXIgcm9vdCBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEH\r\nA0IABNJHM5ZE+fpVn7b9WwjVBiOiZq9eNXq1JkNj/6ZLe+2GkaRY/WE2Xbg7yx++\r\nh3QEdX3sGKzGO7dygQALBe/4qEyjgfMwgfAwHQYDVR0OBBYEFK8ogqdUH2vZlC1y\r\nNf619a8fnx8KMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMHsG\r\nA1UdHwR0MHIwcKBuoGyGamh0dHBzOi8vbGVhcm4udmlpLmF1MDEubWF0dHIuZ2xv\r\nYmFsL3YyL3ByZXNlbnRhdGlvbnMvY2VydGlmaWNhdGVzL2Q3YzE3ODI4LThkMTgt\r\nNDYyZS1iNDk3LWNjNjI2NWM4ZmQxYi9jcmwwLgYDVR0SBCcwJYYjaHR0cHM6Ly9s\r\nZWFybi52aWkuYXUwMS5tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSAAwRQIhAOqD\r\n0DF3rohBitl5jAj6x1164uGGj6yAhF/eE4aJeGc+AiAgaUYHzobzaPEWd+jZOh/A\r\nq8WgVJ+8sLx9WdJDs9/shQ==\r\n-----END CERTIFICATE-----\r\n",
"status": "Inactive",
"linkCertificateInfo": {
"issuerCertificateId": "3b5b1a6a-1b1a-4b1a-8b1a-1b1a4b1a8b1a",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n"
}
}
```
### Responses
#### 201 - Participant verifier certificate created
```json
{
"id": "3b5b1a6a-1b1a-4b1a-8b1a-1b1a4b1a8b1a",
"certificateFingerprint": "65A98EFAB9A22A1829391B22B668F9F1B758094C1A19D599B516BBF63CBF0015",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICTDCCAfKgAwIBAgIKeKcjIBGvXfS/sjAKBggqhkjOPQQDAjAwMQswCQYDVQQG\r\nEwJVUzEhMB8GA1UEAwwYRXhhbXBsZSBWZXJpZmllciByb290IENBMB4XDTI1MDgy\r\nNzIxMzMxNFoXDTMwMDgyNjIxMzMxNFowMDELMAkGA1UEBhMCVVMxITAfBgNVBAMM\r\nGEV4YW1wbGUgVmVyaWZpZXIgcm9vdCBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEH\r\nA0IABNJHM5ZE+fpVn7b9WwjVBiOiZq9eNXq1JkNj/6ZLe+2GkaRY/WE2Xbg7yx++\r\nh3QEdX3sGKzGO7dygQALBe/4qEyjgfMwgfAwHQYDVR0OBBYEFK8ogqdUH2vZlC1y\r\nNf619a8fnx8KMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMHsG\r\nA1UdHwR0MHIwcKBuoGyGamh0dHBzOi8vbGVhcm4udmlpLmF1MDEubWF0dHIuZ2xv\r\nYmFsL3YyL3ByZXNlbnRhdGlvbnMvY2VydGlmaWNhdGVzL2Q3YzE3ODI4LThkMTgt\r\nNDYyZS1iNDk3LWNjNjI2NWM4ZmQxYi9jcmwwLgYDVR0SBCcwJYYjaHR0cHM6Ly9s\r\nZWFybi52aWkuYXUwMS5tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSAAwRQIhAOqD\r\n0DF3rohBitl5jAj6x1164uGGj6yAhF/eE4aJeGc+AiAgaUYHzobzaPEWd+jZOh/A\r\nq8WgVJ+8sLx9WdJDs9/shQ==\r\n-----END CERTIFICATE-----\r\n",
"status": "Inactive",
"linkCertificateInfo": {
"issuerCertificateId": "3b5b1a6a-1b1a-4b1a-8b1a-1b1a4b1a8b1a",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"certificateFingerprint": "57B178A6C2B8C1877DBA515AD4FD60F9C805EFC309287182DB7DEBFE43A22928"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a participant verifier certificate
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/participants/{participantId}/verifier-certificates/{verifierCertificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/verifier-certificates/{verifierCertificateId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieve a specific verifier certificate for the specified participant.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_VERIFIER_CERTIFICATE_RETRIEVE_START
* ECOSYSTEM_PARTICIPANT_VERIFIER_CERTIFICATE_RETRIEVE_SUCCESS
* ECOSYSTEM_PARTICIPANT_VERIFIER_CERTIFICATE_RETRIEVE_FAIL
### Responses
#### 200 - Participant verifier certificate retrieved
```json
{
"id": "3b5b1a6a-1b1a-4b1a-8b1a-1b1a4b1a8b1a",
"certificateFingerprint": "65A98EFAB9A22A1829391B22B668F9F1B758094C1A19D599B516BBF63CBF0015",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICTDCCAfKgAwIBAgIKeKcjIBGvXfS/sjAKBggqhkjOPQQDAjAwMQswCQYDVQQG\r\nEwJVUzEhMB8GA1UEAwwYRXhhbXBsZSBWZXJpZmllciByb290IENBMB4XDTI1MDgy\r\nNzIxMzMxNFoXDTMwMDgyNjIxMzMxNFowMDELMAkGA1UEBhMCVVMxITAfBgNVBAMM\r\nGEV4YW1wbGUgVmVyaWZpZXIgcm9vdCBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEH\r\nA0IABNJHM5ZE+fpVn7b9WwjVBiOiZq9eNXq1JkNj/6ZLe+2GkaRY/WE2Xbg7yx++\r\nh3QEdX3sGKzGO7dygQALBe/4qEyjgfMwgfAwHQYDVR0OBBYEFK8ogqdUH2vZlC1y\r\nNf619a8fnx8KMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMHsG\r\nA1UdHwR0MHIwcKBuoGyGamh0dHBzOi8vbGVhcm4udmlpLmF1MDEubWF0dHIuZ2xv\r\nYmFsL3YyL3ByZXNlbnRhdGlvbnMvY2VydGlmaWNhdGVzL2Q3YzE3ODI4LThkMTgt\r\nNDYyZS1iNDk3LWNjNjI2NWM4ZmQxYi9jcmwwLgYDVR0SBCcwJYYjaHR0cHM6Ly9s\r\nZWFybi52aWkuYXUwMS5tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSAAwRQIhAOqD\r\n0DF3rohBitl5jAj6x1164uGGj6yAhF/eE4aJeGc+AiAgaUYHzobzaPEWd+jZOh/A\r\nq8WgVJ+8sLx9WdJDs9/shQ==\r\n-----END CERTIFICATE-----\r\n",
"status": "Inactive",
"linkCertificateInfo": {
"issuerCertificateId": "3b5b1a6a-1b1a-4b1a-8b1a-1b1a4b1a8b1a",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"certificateFingerprint": "57B178A6C2B8C1877DBA515AD4FD60F9C805EFC309287182DB7DEBFE43A22928"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a participant verifier certificate
## Endpoint
```
DELETE /v1/ecosystems/{ecosystemId}/participants/{participantId}/verifier-certificates/{verifierCertificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/verifier-certificates/{verifierCertificateId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Delete a specific verifier certificate for the specified participant.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_VERIFIER_CERTIFICATE_DELETE_START
* ECOSYSTEM_PARTICIPANT_VERIFIER_CERTIFICATE_DELETE_SUCCESS
* ECOSYSTEM_PARTICIPANT_VERIFIER_CERTIFICATE_DELETE_FAIL
### Responses
#### 204 - Participant verifier certificate deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a participant verifier certificate
## Endpoint
```
PUT /v1/ecosystems/{ecosystemId}/participants/{participantId}/verifier-certificates/{verifierCertificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/participants/{participantId}/verifier-certificates/{verifierCertificateId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Update a specific verifier certificate for the specified participant.
### **Analytic events**
* ECOSYSTEM_PARTICIPANT_VERIFIER_CERTIFICATE_UPDATE_START
* ECOSYSTEM_PARTICIPANT_VERIFIER_CERTIFICATE_UPDATE_SUCCESS
* ECOSYSTEM_PARTICIPANT_VERIFIER_CERTIFICATE_UPDATE_FAIL
### Request Body
```json
{
"status": "Active"
}
```
### Responses
#### 200 - Participant verifier certificate updated
```json
{
"id": "3b5b1a6a-1b1a-4b1a-8b1a-1b1a4b1a8b1a",
"certificateFingerprint": "65A98EFAB9A22A1829391B22B668F9F1B758094C1A19D599B516BBF63CBF0015",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIICTDCCAfKgAwIBAgIKeKcjIBGvXfS/sjAKBggqhkjOPQQDAjAwMQswCQYDVQQG\r\nEwJVUzEhMB8GA1UEAwwYRXhhbXBsZSBWZXJpZmllciByb290IENBMB4XDTI1MDgy\r\nNzIxMzMxNFoXDTMwMDgyNjIxMzMxNFowMDELMAkGA1UEBhMCVVMxITAfBgNVBAMM\r\nGEV4YW1wbGUgVmVyaWZpZXIgcm9vdCBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEH\r\nA0IABNJHM5ZE+fpVn7b9WwjVBiOiZq9eNXq1JkNj/6ZLe+2GkaRY/WE2Xbg7yx++\r\nh3QEdX3sGKzGO7dygQALBe/4qEyjgfMwgfAwHQYDVR0OBBYEFK8ogqdUH2vZlC1y\r\nNf619a8fnx8KMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMHsG\r\nA1UdHwR0MHIwcKBuoGyGamh0dHBzOi8vbGVhcm4udmlpLmF1MDEubWF0dHIuZ2xv\r\nYmFsL3YyL3ByZXNlbnRhdGlvbnMvY2VydGlmaWNhdGVzL2Q3YzE3ODI4LThkMTgt\r\nNDYyZS1iNDk3LWNjNjI2NWM4ZmQxYi9jcmwwLgYDVR0SBCcwJYYjaHR0cHM6Ly9s\r\nZWFybi52aWkuYXUwMS5tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSAAwRQIhAOqD\r\n0DF3rohBitl5jAj6x1164uGGj6yAhF/eE4aJeGc+AiAgaUYHzobzaPEWd+jZOh/A\r\nq8WgVJ+8sLx9WdJDs9/shQ==\r\n-----END CERTIFICATE-----\r\n",
"status": "Inactive",
"linkCertificateInfo": {
"issuerCertificateId": "3b5b1a6a-1b1a-4b1a-8b1a-1b1a4b1a8b1a",
"certificatePem": "-----BEGIN CERTIFICATE-----\r\nMIIBwzCCAWigAwIBAgIKRGC+CqoTGJKkkTAKBggqhkjOPQQDAjAgMR4wCQYDVQQG\r\nEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwHhcNMjMwODA4MDAwOTIxWhcNMzMwODA1\r\nMDAwOTIxWjAgMR4wCQYDVQQGEwJOWjARBgNVBAMTCk1BVFRSIElBQ0EwWTATBgcq\r\nhkjOPQIBBggqhkjOPQMBBwNCAASRu69fzdgM4odkyPtRcZd3eGWCw4BB7StZNGRm\r\nuIlrraUyv9SWPHgUYjYmRB1g7ERzj/pOSAspk71Y+QA+j9nPo4GJMIGGMBIGA1Ud\r\nEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgAGMB0GA1UdDgQWBBSONcHGh4If\r\nO1dYorRpsuFrs+f8SDAcBgNVHRIEFTATgRFpbmZvQG1hdHRyLmdsb2JhbDAjBgNV\r\nHR8EHDAaMBiiFoYUaHR0cHM6Ly9tYXR0ci5nbG9iYWwwCgYIKoZIzj0EAwIDSQAw\r\nRgIhAPKJIGDSvp7VxRBLCWWeghqi8UUeO+dZsC49TUZcDMNxAiEAoh+7dT+l+GzX\r\nk0J2SoGmPiagrbAuIYyTHwzZZuYr1W4=\r\n-----END CERTIFICATE-----\r\n",
"certificateFingerprint": "57B178A6C2B8C1877DBA515AD4FD60F9C805EFC309287182DB7DEBFE43A22928"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create VICAL
## Endpoint
```
POST /v1/ecosystems/{ecosystemId}/vicals
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/vicals`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Creates a Verified Issuer Certificate Authority List (VICAL) based on the policy of the requested ecosystem. Refer to VICAL to learn more about the [VICAL](https://learn.mattr.global/docs/digital-trust-service/vical-overview) purpose and data structure.
### Responses
#### 201 - VICAL created
```json
{
"vicalIssueID": 1337,
"date": "2024-07-28T23:01:13.000Z"
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Retrieve all VICALs
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/vicals/public
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/vicals/public`
### Authorization
None required.
## Description
Retrieves all VICALs available in the requested ecosystem.
This endpoint is intended for public consumption, and as such does not require authentication.
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - VICALs retrieved
```json
{
"data": [
{
"vicalIssueID": 1337,
"date": "2024-07-28T23:01:13.000Z",
"filename": "vical-2024-07-28-1722164473000.cbor",
"isAutoPublished": false
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve latest VICAL
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/vicals/public/latest
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/vicals/public/latest`
### Authorization
None required.
## Description
Retrieves the latest VICAL from the requested ecosystem.
This endpoint is intended for public consumption, and as such does not require authentication.
### Responses
#### 200 - VICAL retrieved
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve specific VICAL
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/vicals/public/{vicalIssueId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/vicals/public/{vicalIssueId}`
### Authorization
None required.
## Description
Retrieves a specific VICAL from the requested ecosystem by providing the VICAL version identifier.
This endpoint is intended for public consumption, and as such does not require authentication.
### Responses
#### 200 - VICAL retrieved
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve VICAL configuration
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/vicals/configuration
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/vicals/configuration`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieve the VICAL configuration from the requested ecosystem
### Responses
#### 200 - VICAL configuration retrieved
```json
{
"vicalProvider": "Mattr",
"autoPublish": {
"configuredAt": "2025-07-01T00:00:00.000Z"
}
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete VICAL configuration
## Endpoint
```
DELETE /v1/ecosystems/{ecosystemId}/vicals/configuration
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/vicals/configuration`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Delete the VICAL configuration for the requested ecosystem
### Responses
#### 204 - VICAL configuration deleted
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update VICAL configuration
## Endpoint
```
PUT /v1/ecosystems/{ecosystemId}/vicals/configuration
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/vicals/configuration`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Update the VICAL configuration for the requested ecosystem
### Request Body
```json
{
"vicalProvider": "Mattr",
"autoPublish": {
"enabled": true,
"frequency": "Daily"
}
}
```
### Responses
#### 200 - VICAL configuration updated
```json
{
"vicalProvider": "Mattr",
"autoPublish": {
"configuredAt": "2025-07-01T00:00:00.000Z"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all DTS root CA certificates
## Endpoint
```
GET /v1/ecosystems/certificates/ca
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/certificates/ca`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieves all DTS root CA certificates.
### **Analytic events**
* ECOSYSTEM_DTS_CA_CERTIFICATE_RETRIEVE_LIST_START
* ECOSYSTEM_DTS_CA_CERTIFICATE_RETRIEVE_LIST_SUCCESS
* ECOSYSTEM_DTS_CA_CERTIFICATE_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - DTS root CA certificates retrieved
```json
{
"data": [
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"certificateFingerprint": "f6cad6e579d70b3973efa60624af731a580d1a11a7579e70f2f10f059dc86172",
"certificateData": {
"commonName": "example.com",
"country": "US",
"organisationName": "Example Inc.",
"notAfter": "2024-10-22T00:00:00Z",
"notBefore": "2023-10-22T00:00:00Z"
},
"isManaged": true,
"useIntermediateCa": true
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create a DTS root CA certificate
## Endpoint
```
POST /v1/ecosystems/certificates/ca
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/certificates/ca`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Creates a DTS root CA certificate which is used to sign DTS intermediate and/or signer certificates.
- A maximum of three DTS root CA certificates can be created per tenant.
### **Analytic events**
* ECOSYSTEM_DTS_CA_CERTIFICATE_CREATE_START
* ECOSYSTEM_DTS_CA_CERTIFICATE_CREATE_SUCCESS
* ECOSYSTEM_DTS_CA_CERTIFICATE_CREATE_FAIL
### Request Body
### Responses
#### 201 - DTS root CA certificate created
```json
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"certificateFingerprint": "f6cad6e579d70b3973efa60624af731a580d1a11a7579e70f2f10f059dc86172",
"certificateData": {
"commonName": "example.com",
"country": "US",
"organisationName": "Example Inc.",
"notAfter": "2024-10-22T00:00:00Z",
"notBefore": "2023-10-22T00:00:00Z"
},
"isManaged": true,
"useIntermediateCa": true
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 409 - Maximum number of DTS CA certificates reached. Please delete an existing certificate before creating a new one.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a DTS root CA certificate
## Endpoint
```
GET /v1/ecosystems/certificates/ca/{dtsCaCertificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/certificates/ca/{dtsCaCertificateId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieves a DTS root CA certificate.
### **Analytic events**
* ECOSYSTEM_DTS_CA_CERTIFICATE_RETRIEVE_START
* ECOSYSTEM_DTS_CA_CERTIFICATE_RETRIEVE_SUCCESS
* ECOSYSTEM_DTS_CA_CERTIFICATE_RETRIEVE_FAIL
### Responses
#### 200 - DTS root CA certificate retrieved
```json
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"certificateFingerprint": "f6cad6e579d70b3973efa60624af731a580d1a11a7579e70f2f10f059dc86172",
"certificateData": {
"commonName": "example.com",
"country": "US",
"organisationName": "Example Inc.",
"notAfter": "2024-10-22T00:00:00Z",
"notBefore": "2023-10-22T00:00:00Z"
},
"isManaged": true,
"useIntermediateCa": true
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a DTS root CA certificate
## Endpoint
```
DELETE /v1/ecosystems/certificates/ca/{dtsCaCertificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/certificates/ca/{dtsCaCertificateId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Deletes a DTS root CA certificate.
### **Analytic events**
* ECOSYSTEM_DTS_CA_CERTIFICATE_DELETE_START
* ECOSYSTEM_DTS_CA_CERTIFICATE_DELETE_SUCCESS
* ECOSYSTEM_DTS_CA_CERTIFICATE_DELETE_FAIL
### Responses
#### 204 - DTS root CA certificate deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a DTS root CA certificate
## Endpoint
```
PUT /v1/ecosystems/certificates/ca/{dtsCaCertificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/certificates/ca/{dtsCaCertificateId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Updates a DTS root CA certificate.
### **Analytic events**
* ECOSYSTEM_DTS_CA_CERTIFICATE_UPDATE_START
* ECOSYSTEM_DTS_CA_CERTIFICATE_UPDATE_SUCCESS
* ECOSYSTEM_DTS_CA_CERTIFICATE_UPDATE_FAIL
### Request Body
```json
{
"active": true,
"useIntermediateCa": true
}
```
### Responses
#### 200 - DTS root CA certificate updated
```json
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"certificateFingerprint": "f6cad6e579d70b3973efa60624af731a580d1a11a7579e70f2f10f059dc86172",
"certificateData": {
"commonName": "example.com",
"country": "US",
"organisationName": "Example Inc.",
"notAfter": "2024-10-22T00:00:00Z",
"notBefore": "2023-10-22T00:00:00Z"
},
"isManaged": true,
"useIntermediateCa": true
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a DTS root CA certificate revocation list
## Endpoint
```
GET /v1/ecosystems/certificates/ca/{dtsCaCertificateId}/crl
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/certificates/ca/{dtsCaCertificateId}/crl`
### Authorization
None required.
## Description
Retrieves the revocation list for a given DTS root CA certificate.
This endpoint is intended for public consumption, and as such does not require authentication.
### Responses
#### 200 - DTS root CA certificate revocation list retrieved
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all public DTS root CA certificates
## Endpoint
```
GET /v1/ecosystems/public/certificates/ca
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/public/certificates/ca`
### Authorization
None required.
## Description
Retrieves all public DTS root CA certificates.
This endpoint is intended for public consumption, and as such does not require authentication.
### Responses
#### 200 - Public DTS root CA certificates retrieved
```json
{
"rootCertificates": [
{
"certificate": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"notBefore": "2023-10-22T00:00:00Z",
"notAfter": "2024-10-22T00:00:00Z",
"fingerprint": "f6cad6e579d70b3973efa60624af731a580d1a11a7579e70f2f10f059dc86172",
"commonName": "MATTR DTS Root CA",
"intermediateCertificates": [
{
"certificate": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"notBefore": "2023-10-22T00:00:00Z",
"notAfter": "2024-10-22T00:00:00Z",
"fingerprint": "f6cad6e579d70b3973efa60624af731a580d1a11a7579e70f2f10f059dc86172",
"commonName": "MATTR DTS Intermediate CA"
}
]
}
]
}
```
# Retrieve DTS root CA certificate
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/vicals/public/certificates/ca/latest
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/vicals/public/certificates/ca/latest`
### Authorization
None required.
## Description
Retrieves the latest DTS root CA certificate. This can be used by relying parties to verify a signed VICAL.
This endpoint is intended for public consumption, and as such does not require authentication.
### Responses
#### 200 - DTS root CA certificate retrieved
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve DTS root CA certificate revocation list
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/vicals/public/certificates/ca/{caCertificateId}/crl
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/vicals/public/certificates/ca/{caCertificateId}/crl`
### Authorization
None required.
## Description
Retrieves revocation list for a given DTS root CA certificate.
This endpoint is intended for public consumption, and as such does not require authentication.
### Responses
#### 200 - Revocation list retrieved
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all DTS intermediate CA certificates under a root CA
## Endpoint
```
GET /v1/ecosystems/certificates/ca/{dtsCaCertificateId}/intermediate
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/certificates/ca/{dtsCaCertificateId}/intermediate`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieves all DTS intermediate CA certificates issued under the specified DTS root CA.
### **Analytic events**
* ECOSYSTEM_DTS_INTERMEDIATE_CA_CERTIFICATE_RETRIEVE_LIST_START
* ECOSYSTEM_DTS_INTERMEDIATE_CA_CERTIFICATE_RETRIEVE_LIST_SUCCESS
* ECOSYSTEM_DTS_INTERMEDIATE_CA_CERTIFICATE_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - DTS intermediate CA certificates retrieved
```json
{
"data": [
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"certificateFingerprint": "f6cad6e579d70b3973efa60624af731a580d1a11a7579e70f2f10f059dc86172",
"certificateData": {
"commonName": "example.com",
"country": "US",
"organisationName": "Example Inc.",
"notAfter": "2024-10-22T00:00:00Z",
"notBefore": "2023-10-22T00:00:00Z"
}
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create a DTS intermediate CA certificate
## Endpoint
```
POST /v1/ecosystems/certificates/ca/{dtsCaCertificateId}/intermediate
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/certificates/ca/{dtsCaCertificateId}/intermediate`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Creates a DTS intermediate CA certificate, which sits between the DTS root CA certificate and the signer certificate to sign DTS signer certificates.
DTS intermediate CA certificates are only relevant when using the 3-tier certificate model, where a DTS root CA signs an intermediate CA, which in turn signs the signer certificate. They are only supported for unmanaged (external) DTS root CA certificates that have `useIntermediateCa` set to `true`. For an explanation of the 2-tier and 3-tier models and guidance on choosing between them, refer to the [DTS certificates overview](https://learn.mattr.global/docs/digital-trust-service/certificates-overview#certificate-models-2-tier-and-3-tier).
### **Analytic events**
* ECOSYSTEM_DTS_INTERMEDIATE_CA_CERTIFICATE_CREATE_START
* ECOSYSTEM_DTS_INTERMEDIATE_CA_CERTIFICATE_CREATE_SUCCESS
* ECOSYSTEM_DTS_INTERMEDIATE_CA_CERTIFICATE_CREATE_FAIL
### Request Body
### Responses
#### 201 - DTS intermediate CA certificate created
```json
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"certificateFingerprint": "f6cad6e579d70b3973efa60624af731a580d1a11a7579e70f2f10f059dc86172",
"certificateData": {
"commonName": "example.com",
"country": "US",
"organisationName": "Example Inc.",
"notAfter": "2024-10-22T00:00:00Z",
"notBefore": "2023-10-22T00:00:00Z"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a DTS intermediate CA certificate
## Endpoint
```
GET /v1/ecosystems/certificates/ca/{dtsCaCertificateId}/intermediate/{dtsIntermediateCaCertificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/certificates/ca/{dtsCaCertificateId}/intermediate/{dtsIntermediateCaCertificateId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieves a DTS intermediate CA certificate.
### **Analytic events**
* ECOSYSTEM_DTS_INTERMEDIATE_CA_CERTIFICATE_RETRIEVE_START
* ECOSYSTEM_DTS_INTERMEDIATE_CA_CERTIFICATE_RETRIEVE_SUCCESS
* ECOSYSTEM_DTS_INTERMEDIATE_CA_CERTIFICATE_RETRIEVE_FAIL
### Responses
#### 200 - DTS intermediate CA certificate retrieved
```json
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"certificateFingerprint": "f6cad6e579d70b3973efa60624af731a580d1a11a7579e70f2f10f059dc86172",
"certificateData": {
"commonName": "example.com",
"country": "US",
"organisationName": "Example Inc.",
"notAfter": "2024-10-22T00:00:00Z",
"notBefore": "2023-10-22T00:00:00Z"
}
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a DTS intermediate CA certificate
## Endpoint
```
DELETE /v1/ecosystems/certificates/ca/{dtsCaCertificateId}/intermediate/{dtsIntermediateCaCertificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/certificates/ca/{dtsCaCertificateId}/intermediate/{dtsIntermediateCaCertificateId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Deletes a DTS intermediate CA certificate.
### **Analytic events**
* ECOSYSTEM_DTS_INTERMEDIATE_CA_CERTIFICATE_DELETE_START
* ECOSYSTEM_DTS_INTERMEDIATE_CA_CERTIFICATE_DELETE_SUCCESS
* ECOSYSTEM_DTS_INTERMEDIATE_CA_CERTIFICATE_DELETE_FAIL
### Responses
#### 204 - DTS intermediate CA certificate deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a DTS intermediate CA certificate
## Endpoint
```
PUT /v1/ecosystems/certificates/ca/{dtsCaCertificateId}/intermediate/{dtsIntermediateCaCertificateId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/certificates/ca/{dtsCaCertificateId}/intermediate/{dtsIntermediateCaCertificateId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Updates a DTS intermediate CA certificate.
### **Analytic events**
* ECOSYSTEM_DTS_INTERMEDIATE_CA_CERTIFICATE_UPDATE_START
* ECOSYSTEM_DTS_INTERMEDIATE_CA_CERTIFICATE_UPDATE_SUCCESS
* ECOSYSTEM_DTS_INTERMEDIATE_CA_CERTIFICATE_UPDATE_FAIL
### Request Body
```json
{
"active": true
}
```
### Responses
#### 200 - DTS intermediate CA certificate updated
```json
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"certificateFingerprint": "f6cad6e579d70b3973efa60624af731a580d1a11a7579e70f2f10f059dc86172",
"certificateData": {
"commonName": "example.com",
"country": "US",
"organisationName": "Example Inc.",
"notAfter": "2024-10-22T00:00:00Z",
"notBefore": "2023-10-22T00:00:00Z"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all VICAL signers
## Endpoint
```
GET /v1/ecosystems/certificates/vical-signers
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/certificates/vical-signers`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieves all VICAL signers.
### **Analytic events**
* ECOSYSTEM_VICAL_SIGNER_CERTIFICATE_RETRIEVE_LIST_START
* ECOSYSTEM_VICAL_SIGNER_CERTIFICATE_RETRIEVE_LIST_SUCCESS
* ECOSYSTEM_VICAL_SIGNER_CERTIFICATE_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - VICAL signers retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create a VICAL signer
## Endpoint
```
POST /v1/ecosystems/certificates/vical-signers
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/certificates/vical-signers`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Creates a VICAL signer.
- Only available in implementations using unmanaged (external) DTS root CA certificates.
- A maximum of five VICAL signers can be created per tenant.
### **Analytic events**
* ECOSYSTEM_VICAL_SIGNER_CERTIFICATE_CREATE_START
* ECOSYSTEM_VICAL_SIGNER_CERTIFICATE_CREATE_SUCCESS
* ECOSYSTEM_VICAL_SIGNER_CERTIFICATE_CREATE_FAIL
### Request Body
```json
{
"caId": "b0aae560-10e7-4247-8e96-7cdd3578a1e2",
"intermediateCaId": "b0aae560-10e7-4247-8e96-7cdd3578a1e2"
}
```
### Responses
#### 201 - VICAL signer created
```json
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"csrPem": "-----BEGIN CERTIFICATE REQUEST-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE REQUEST-----",
"caId": "b0aae560-10e7-4247-8e96-7cdd3578a1e2",
"intermediateCaId": "b0aae560-10e7-4247-8e96-7cdd3578a1e2",
"active": false
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 409 - Maximum number of VICAL signer certificates reached. Please delete an existing certificate before creating a new one.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a VICAL signer
## Endpoint
```
GET /v1/ecosystems/certificates/vical-signers/{vicalSignerId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/certificates/vical-signers/{vicalSignerId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieves a VICAL signer.
### **Analytic events**
* ECOSYSTEM_VICAL_SIGNER_CERTIFICATE_RETRIEVE_START
* ECOSYSTEM_VICAL_SIGNER_CERTIFICATE_RETRIEVE_SUCCESS
* ECOSYSTEM_VICAL_SIGNER_CERTIFICATE_RETRIEVE_FAIL
### Responses
#### 200 - VICAL signer retrieved
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a VICAL signer
## Endpoint
```
DELETE /v1/ecosystems/certificates/vical-signers/{vicalSignerId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/certificates/vical-signers/{vicalSignerId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Deletes a VICAL signer.
Only available in implementations using unmanaged (external) DTS root CA certificates.
### **Analytic events**
* ECOSYSTEM_VICAL_SIGNER_CERTIFICATE_DELETE_START
* ECOSYSTEM_VICAL_SIGNER_CERTIFICATE_DELETE_SUCCESS
* ECOSYSTEM_VICAL_SIGNER_CERTIFICATE_DELETE_FAIL
### Responses
#### 204 - VICAL signer deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a VICAL signer
## Endpoint
```
PUT /v1/ecosystems/certificates/vical-signers/{vicalSignerId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/certificates/vical-signers/{vicalSignerId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Updates a VICAL signer by:
- Providing a VICAL Signer Certificate (VSC) in PEM format that matches its Certificate Signing Request (CSR).
- Activating or deactivating the VICAL signer. Only VICAL signers with a valid `certificatePem` can be activated.
- The `certificatePem` field becomes immutable after it's updated for the first time.
Only available in implementations using unmanaged (external) DTS root CA certificates.
### **Analytic events**
* ECOSYSTEM_VICAL_SIGNER_CERTIFICATE_UPDATE_START
* ECOSYSTEM_VICAL_SIGNER_CERTIFICATE_UPDATE_SUCCESS
* ECOSYSTEM_VICAL_SIGNER_CERTIFICATE_UPDATE_FAIL
### Request Body
```json
{
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----"
}
```
### Responses
#### 200 - VICAL signer updated
```json
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"certificateFingerprint": "f6cad6e579d70b3973efa60624af731a580d1a11a7579e70f2f10f059dc86172",
"certificateData": {
"commonName": "example.com",
"country": "US",
"notAfter": "2024-10-22T00:00:00Z",
"notBefore": "2023-10-22T00:00:00Z"
},
"caId": "b0aae560-10e7-4247-8e96-7cdd3578a1e2",
"intermediateCaId": "b0aae560-10e7-4247-8e96-7cdd3578a1e2"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create RICAL
## Endpoint
```
POST /v1/ecosystems/{ecosystemId}/ricals
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/ricals`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Creates a Reader Identity Certificate Authority List (RICAL) based on the policy of the requested ecosystem.
A [RICAL configuration](#operation/updateRicalConfiguration) is required in order for RICALs to be created.
### **Analytic events**
* ECOSYSTEM_RICAL_CREATE_START
* ECOSYSTEM_RICAL_CREATE_SUCCESS
* ECOSYSTEM_RICAL_CREATE_FAIL
### Responses
#### 201 - RICAL created
```json
{
"ricalIssueID": 1337,
"date": "2024-07-28T23:01:13.000Z"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Retrieve all RICALs
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/ricals/public
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/ricals/public`
### Authorization
None required.
## Description
Retrieves all RICALs available in the requested ecosystem.
The response is a JSON list of the published RICALs and their version identifiers. To download an actual RICAL file (a CBOR encoded file matching the RICAL format defined in ISO/IEC 18013-5), call the [Retrieve specific RICAL](#operation/getRical) or [Retrieve latest RICAL](#operation/getLatestRical) endpoint.
This endpoint is intended for public consumption, and as such does not require authentication.
### **Analytic events**
* ECOSYSTEM_RICAL_RETRIEVE_LIST_START
* ECOSYSTEM_RICAL_RETRIEVE_LIST_SUCCESS
* ECOSYSTEM_RICAL_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - RICALs retrieved
```json
{
"data": [
{
"ricalIssueID": 1337,
"date": "2024-07-28T23:01:13.000Z",
"filename": "rical-2024-07-28-1722164473000.cbor",
"isAutoPublished": false
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve latest RICAL
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/ricals/public/latest
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/ricals/public/latest`
### Authorization
None required.
## Description
Retrieves the latest RICAL from the requested ecosystem.
The response is a CBOR encoded file matching the RICAL format defined in ISO/IEC 18013-5.
This endpoint is intended for public consumption, and as such does not require authentication.
### **Analytic events**
* ECOSYSTEM_RICAL_RETRIEVE_LATEST_START
* ECOSYSTEM_RICAL_RETRIEVE_LATEST_SUCCESS
* ECOSYSTEM_RICAL_RETRIEVE_LATEST_FAIL
### Responses
#### 200 - RICAL retrieved
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve specific RICAL
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/ricals/public/{ricalIssueId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/ricals/public/{ricalIssueId}`
### Authorization
None required.
## Description
Retrieves a specific RICAL from the requested ecosystem by providing the RICAL version identifier.
The response is a CBOR encoded file matching the RICAL format defined in ISO/IEC 18013-5.
This endpoint is intended for public consumption, and as such does not require authentication.
### **Analytic events**
* ECOSYSTEM_RICAL_RETRIEVE_START
* ECOSYSTEM_RICAL_RETRIEVE_SUCCESS
* ECOSYSTEM_RICAL_RETRIEVE_FAIL
### Responses
#### 200 - RICAL retrieved
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve RICAL configuration
## Endpoint
```
GET /v1/ecosystems/{ecosystemId}/ricals/configuration
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/ricals/configuration`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieve the RICAL configuration from the requested ecosystem.
### **Analytic events**
* ECOSYSTEM_RICAL_CONFIGURATION_RETRIEVE_START
* ECOSYSTEM_RICAL_CONFIGURATION_RETRIEVE_SUCCESS
* ECOSYSTEM_RICAL_CONFIGURATION_RETRIEVE_FAIL
### Responses
#### 200 - RICAL configuration retrieved
```json
{
"ricalProvider": "MATTR",
"autoPublish": {
"configuredAt": "2025-07-01T00:00:00.000Z"
}
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete RICAL configuration
## Endpoint
```
DELETE /v1/ecosystems/{ecosystemId}/ricals/configuration
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/ricals/configuration`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Delete the RICAL configuration for the requested ecosystem.
### **Analytic events**
* ECOSYSTEM_RICAL_CONFIGURATION_DELETE_START
* ECOSYSTEM_RICAL_CONFIGURATION_DELETE_SUCCESS
* ECOSYSTEM_RICAL_CONFIGURATION_DELETE_FAIL
### Responses
#### 204 - RICAL configuration deleted
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update RICAL configuration
## Endpoint
```
PUT /v1/ecosystems/{ecosystemId}/ricals/configuration
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/ricals/configuration`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Update the RICAL configuration for the requested ecosystem.
A RICAL configuration is required in order for RICALs to be created.
### **Analytic events**
* ECOSYSTEM_RICAL_CONFIGURATION_UPSERT_START
* ECOSYSTEM_RICAL_CONFIGURATION_UPSERT_SUCCESS
* ECOSYSTEM_RICAL_CONFIGURATION_UPSERT_FAIL
### Request Body
```json
{
"ricalProvider": "MATTR",
"autoPublish": {
"enabled": true,
"frequency": "Daily"
}
}
```
### Responses
#### 200 - RICAL configuration updated
```json
{
"ricalProvider": "MATTR",
"autoPublish": {
"configuredAt": "2025-07-01T00:00:00.000Z"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all RICAL signers
## Endpoint
```
GET /v1/ecosystems/certificates/rical-signers
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/certificates/rical-signers`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieves all RICAL signers.
### **Analytic events**
* ECOSYSTEM_RICAL_SIGNER_CERTIFICATE_RETRIEVE_LIST_START
* ECOSYSTEM_RICAL_SIGNER_CERTIFICATE_RETRIEVE_LIST_SUCCESS
* ECOSYSTEM_RICAL_SIGNER_CERTIFICATE_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - RICAL signers retrieved
```json
{
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create a RICAL signer
## Endpoint
```
POST /v1/ecosystems/certificates/rical-signers
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/certificates/rical-signers`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Creates a RICAL signer.
- Only available in implementations using unmanaged (external) DTS root CA certificates.
- A maximum of five RICAL signers can be created per tenant.
Provide either `caId` or `intermediateCaId`.
### **Analytic events**
* ECOSYSTEM_RICAL_SIGNER_CERTIFICATE_CREATE_START
* ECOSYSTEM_RICAL_SIGNER_CERTIFICATE_CREATE_SUCCESS
* ECOSYSTEM_RICAL_SIGNER_CERTIFICATE_CREATE_FAIL
### Request Body
```json
{
"caId": "b0aae560-10e7-4247-8e96-7cdd3578a1e2",
"intermediateCaId": "c1bbe671-21f8-5358-9537-eed4669b43f3"
}
```
### Responses
#### 201 - RICAL signer created
```json
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"csrPem": "-----BEGIN CERTIFICATE REQUEST-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE REQUEST-----",
"caId": "b0aae560-10e7-4247-8e96-7cdd3578a1e2",
"intermediateCaId": "c1bbe671-21f8-5358-9537-eed4669b43f3",
"active": false
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 409 - Maximum number of RICAL signer certificates reached. Please delete an existing certificate before creating a new one.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve a RICAL signer
## Endpoint
```
GET /v1/ecosystems/certificates/rical-signers/{ricalSignerId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/certificates/rical-signers/{ricalSignerId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Retrieves a RICAL signer.
### **Analytic events**
* ECOSYSTEM_RICAL_SIGNER_CERTIFICATE_RETRIEVE_START
* ECOSYSTEM_RICAL_SIGNER_CERTIFICATE_RETRIEVE_SUCCESS
* ECOSYSTEM_RICAL_SIGNER_CERTIFICATE_RETRIEVE_FAIL
### Responses
#### 200 - RICAL signer retrieved
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete a RICAL signer
## Endpoint
```
DELETE /v1/ecosystems/certificates/rical-signers/{ricalSignerId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/certificates/rical-signers/{ricalSignerId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Deletes a RICAL signer.
Deleting a RICAL signer does not affect any RICALs it has already signed — those remain valid. It only means this signer can no longer be used to sign new RICALs.
Only available in implementations using unmanaged (external) DTS root CA certificates.
### **Analytic events**
* ECOSYSTEM_RICAL_SIGNER_CERTIFICATE_DELETE_START
* ECOSYSTEM_RICAL_SIGNER_CERTIFICATE_DELETE_SUCCESS
* ECOSYSTEM_RICAL_SIGNER_CERTIFICATE_DELETE_FAIL
### Responses
#### 204 - RICAL signer deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update a RICAL signer
## Endpoint
```
PUT /v1/ecosystems/certificates/rical-signers/{ricalSignerId}
```
Full URL: `https://example.vii.au01.mattr.global/v1/ecosystems/certificates/rical-signers/{ricalSignerId}`
### Authorization
Bearer token required.
Required roles: admin, dts-provider
## Description
Updates a RICAL signer by:
- Providing a RICAL Signer Certificate in PEM format that matches its Certificate Signing Request (CSR).
- Activating or deactivating the RICAL signer. Only RICAL signers with a valid `certificatePem` can be activated.
- The `certificatePem` field becomes immutable after it's updated for the first time.
Only available in implementations using unmanaged (external) DTS root CA certificates.
### **Analytic events**
* ECOSYSTEM_RICAL_SIGNER_CERTIFICATE_UPDATE_START
* ECOSYSTEM_RICAL_SIGNER_CERTIFICATE_UPDATE_SUCCESS
* ECOSYSTEM_RICAL_SIGNER_CERTIFICATE_UPDATE_FAIL
### Request Body
```json
{
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----"
}
```
### Responses
#### 200 - RICAL signer updated
```json
{
"id": "782f1885-c7c2-4459-8426-b6d7c111b0b1",
"active": true,
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
"certificateFingerprint": "f6cad6e579d70b3973efa60624af731a580d1a11a7579e70f2f10f059dc86172",
"certificateData": {
"commonName": "example.com",
"country": "US",
"notAfter": "2024-10-22T00:00:00Z",
"notBefore": "2023-10-22T00:00:00Z"
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve all Webhooks
## Endpoint
```
GET /v1/webhooks
```
Full URL: `https://example.vii.au01.mattr.global/v1/webhooks`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieves a list of webhooks configured on the tenant.
### **Analytic events**
* WEBHOOK_RETRIEVE_LIST_START
* WEBHOOK_RETRIEVE_LIST_SUCCESS
* WEBHOOK_RETRIEVE_LIST_FAIL
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Webhooks retrieved
```json
{
"data": [
{
"id": "0c099611-19c4-4f29-8724-6b9e5ba1ef7c",
"events": [
"OpenIdCredentialIssued"
],
"url": "https://example.com"
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwMjItMDgtMjJUMDElM0E1OSUzQTE5LjYyMFomaWQ9MGMwOTk2MTEtMTljNC00ZjI5LTg3MjQtNmI5ZTViYTFlZjdj"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Create Webhook
## Endpoint
```
POST /v1/webhooks
```
Full URL: `https://example.vii.au01.mattr.global/v1/webhooks`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Creates a new webhook for this tenant.
### **Analytic events**
* WEBHOOK_CREATE_START
* WEBHOOK_CREATE_SUCCESS
* WEBHOOK_CREATE_FAIL
### Request Body
The webhook payload
```json
{
"events": [
"OpenIdCredentialIssued"
],
"url": "https://example.com"
}
```
### Responses
#### 201 - Webhook created
```json
{
"id": "0c099611-19c4-4f29-8724-6b9e5ba1ef7c",
"events": [
"OpenIdCredentialIssued"
],
"url": "https://example.com"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve Webhook
## Endpoint
```
GET /v1/webhooks/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v1/webhooks/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Retrieve a specific Webhook by providing its ID.
### **Analytic events**
* WEBHOOK_RETRIEVE_START
* WEBHOOK_RETRIEVE_SUCCESS
* WEBHOOK_RETRIEVE_FAIL
### Path Parameters
- `id`: string (required)
The requested Webhook ID.
### Responses
#### 200 - Webhook retrieved
```json
{
"id": "0c099611-19c4-4f29-8724-6b9e5ba1ef7c",
"events": [
"OpenIdCredentialIssued"
],
"url": "https://example.com"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Delete Webhook
## Endpoint
```
DELETE /v1/webhooks/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v1/webhooks/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Deletes a Webhook by providing its ID.
### **Analytic events**
* WEBHOOK_DELETE_START
* WEBHOOK_DELETE_SUCCESS
* WEBHOOK_DELETE_FAIL
### Path Parameters
- `id`: string (required)
Webhook ID
### Responses
#### 204 - Webhook deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Update Webhook
## Endpoint
```
PUT /v1/webhooks/{id}
```
Full URL: `https://example.vii.au01.mattr.global/v1/webhooks/{id}`
### Authorization
Bearer token required.
Required roles: admin, issuer, managed-issuer
## Description
Updates an existing Webhook by providing its ID.
### **Analytic events**
* WEBHOOK_UPDATE_START
* WEBHOOK_UPDATE_SUCCESS
* WEBHOOK_UPDATE_FAIL
### Path Parameters
- `id`: string (required)
Webhook ID
### Request Body
Update Webhook
```json
{
"events": [
"OpenIdCredentialIssued"
],
"url": "https://example.com"
}
```
### Responses
#### 200 - Webhook updated
```json
{
"id": "0c099611-19c4-4f29-8724-6b9e5ba1ef7c",
"events": [
"OpenIdCredentialIssued"
],
"url": "https://example.com"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
# Retrieve Webhook JWKs
## Endpoint
```
GET /v1/webhooks/jwks
```
Full URL: `https://example.vii.au01.mattr.global/v1/webhooks/jwks`
### Authorization
None required.
## Description
Retrieves a list of Webhook JWKs (JSON Web Keys) from the tenant. These keys can be used to verify the HTTP signature and validate the integrity and authorship of generated Webhooks.
This endpoint is intended for public consumption, and as such does not require authentication.
### Responses
#### 200 - Webhook JWKs retrieved
# Create API Auth Token
## Endpoint
```
POST /oauth/token
```
Full URL: `https://example.vii.au01.mattr.global/oauth/token`
### Authorization
None required.
## Description
Authorization endpoint for gaining token used for API requests requiring `bearerAuth`.
You will be provided the required `client_id` and `client_secret` as part of onboarding.
> The returned bearer token will only enable access to endpoints as per your client's defined role. Refer to [Access Control](https://learn.mattr.global/docs/platform-management/access-control) for more information.
### Request Body
```json
{
"client_id": "htf792W4p4MedZbnoWAs51EfqUt4d2",
"client_secret": "d3fYDX7FjPg1D1h2viARXsolPByQ9vMfg8LHylBy8F4s5KJLB4HhHGOxxqJnSj3G",
"audience": "https://learn.vii.au01.mattr.global",
"grant_type": "client_credentials"
}
```
### Responses
#### 200 - Successful response
```json
{
"access_token": "s2dgbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6s2dcaEROemRDf5gbRVEwTTVSVFE0TmtZME9UZzVNVEpDTlVJNFJqRTBPREExTmpZMk1qazFPQSJ9",
"expires_in": 14400,
"token_type": "Bearer"
}
```
#### 401 - Unauthorized
# Create API Auth Token
## Endpoint
```
POST /oauth/token
```
Full URL: `https://example.vii.au01.mattr.global/oauth/token`
### Authorization
None required.
## Description
Authorization endpoint for gaining token used for API requests requiring `bearerAuth`.
You will be provided the required `client_id` and `client_secret` as part of onboarding.
> The returned bearer token will only enable access to endpoints as per your client's defined role. Refer to [Access Control](https://learn.mattr.global/docs/platform-management/access-control) for more information.
### Request Body
```json
{
"client_id": "htf792W4p4MedZbnoWAs51EfqUt4d2",
"client_secret": "d3fYDX7FjPg1D1h2viARXsolPByQ9vMfg8LHylBy8F4s5KJLB4HhHGOxxqJnSj3G",
"audience": "https://learn.vii.au01.mattr.global",
"grant_type": "client_credentials"
}
```
### Responses
#### 200 - Successful response
```json
{
"access_token": "s2dgbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6s2dcaEROemRDf5gbRVEwTTVSVFE0TmtZME9UZzVNVEpDTlVJNFJqRTBPREExTmpZMk1qazFPQSJ9",
"expires_in": 14400,
"token_type": "Bearer"
}
```
#### 401 - Unauthorized
# Retrieve tenant
## Endpoint
```
GET /v1/tenants/{tenantId}
```
Full URL: `https://manage.au01.mattr.global/v1/tenants/{tenantId}`
### Authorization
Bearer token required.
## Description
Retrieves a tenant by its ID.
### **Analytic events**
* TENANT_RETRIEVE_START
* TENANT_RETRIEVE_SUCCESS
* TENANT_RETRIEVE_FAIL
### Path Parameters
- `tenantId`: string (required)
Unique ID of the tenant to retrieve.
### Responses
#### 200 - Tenant retrieved
```json
{
"id": "86cb97a9-5e80-4ed7-af13-a170752bb1ea",
"name": "My Tenant",
"subdomain": "my-tenant.vii.au01.mattr.global",
"environment": {
"id": "fa605282-0223-4ae0-831d-af368bc39a55",
"name": "MATTR Public Sydney, Australia",
"domain": "vii.au01.mattr.global",
"authorizationServerDomain": "manage.auth.auth0.com",
"deploymentModel": "public",
"region": {
"id": "70bb433a-f0ec-4297-ad76-3b09c71311f3",
"name": "AU01",
"displayName": "Sydney, Australia"
}
},
"membership": {
"roles": [
"dts-provider",
"issuer"
]
}
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 401 - Unauthorized. The client is not recognized by authorization server.
#### 403 - Forbidden. The client is recognized by authorization server but is not allowed to access this resource.
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Delete tenant
## Endpoint
```
DELETE /v1/tenants/{tenantId}
```
Full URL: `https://manage.au01.mattr.global/v1/tenants/{tenantId}`
### Authorization
Bearer token required.
## Description
Deletes a tenant by its ID.
### **Analytic events**
* TENANT_DELETE_START
* TENANT_DELETE_SUCCESS
* TENANT_DELETE_FAIL
### Path Parameters
- `tenantId`: string (required)
Unique ID of the tenant to delete.
### Responses
#### 204 - Tenant deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 401 - Unauthorized. The client is not recognized by authorization server.
#### 403 - Forbidden. The client is recognized by authorization server but is not allowed to access this resource.
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Retrieve clients
## Endpoint
```
GET /v1/tenants/{tenantId}/clients
```
Full URL: `https://manage.au01.mattr.global/v1/tenants/{tenantId}/clients`
### Authorization
Bearer token required.
## Description
Retrieves a list of clients authorized to interact with the requested tenant.
### **Analytic events**
* TENANT_CLIENT_RETRIEVE_LIST_START
* TENANT_CLIENT_RETRIEVE_LIST_SUCCESS
* TENANT_CLIENT_RETRIEVE_LIST_FAIL
### Path Parameters
- `tenantId`: string (required)
Unique ID of the tenant to retrieve clients for.
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Clients retrieved
```json
{
"data": [
{
"clientId": "suC7IhmDIawnlqBlEOuIqBWoqppcdI5",
"name": "Example client",
"permissions": [
"dids:read",
"dids:create"
],
"roles": [
"issuer"
]
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 401 - Unauthorized. The client is not recognized by authorization server.
#### 403 - Forbidden. The client is recognized by authorization server but is not allowed to access this resource.
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Create a client
## Endpoint
```
POST /v1/tenants/{tenantId}/clients
```
Full URL: `https://manage.au01.mattr.global/v1/tenants/{tenantId}/clients`
### Authorization
Bearer token required.
## Description
Creates a new client for the requested tenant.
### **Analytic events**
* TENANT_CLIENT_CREATE_START
* TENANT_CLIENT_CREATE_SUCCESS
* TENANT_CLIENT_CREATE_FAIL
### Path Parameters
- `tenantId`: string (required)
Unique ID of the tenant to create a client for.
### Request Body
```json
{
"name": "Example client",
"roles": [
"issuer"
]
}
```
### Responses
#### 201 - Client created
```json
{
"clientId": "suC7IhmDIawnlqBlEOuIqBWoqppcdI5",
"name": "Example client",
"permissions": [
"dids:read",
"dids:create"
],
"roles": [
"issuer"
],
"clientSecret": "QmtShBH3mkO05ra91dNO-YyPwPbfs1iokh57IgqhzVWTAZlolCdeAGYOG2kz1"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
#### 503 - Service Unavailable. The server is temporarily unavailable to handle requests.
# Delete a client
## Endpoint
```
DELETE /v1/tenants/{tenantId}/clients/{clientId}
```
Full URL: `https://manage.au01.mattr.global/v1/tenants/{tenantId}/clients/{clientId}`
### Authorization
Bearer token required.
## Description
Deletes an existing client of the specified tenant.
### **Analytic events**
* TENANT_CLIENT_DELETE_START
* TENANT_CLIENT_DELETE_SUCCESS
* TENANT_CLIENT_DELETE_FAIL
### Path Parameters
- `tenantId`: string (required)
Identifier of the tenant to delete the client from.
- `clientId`: string (required)
Identifier of the client to delete.
### Responses
#### 204 - Client deleted
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
#### 503 - Service Unavailable. The server is temporarily unavailable to handle requests.
# Invite a tenant member
## Endpoint
```
POST /v1/tenants/{tenantId}/invitations
```
Full URL: `https://manage.au01.mattr.global/v1/tenants/{tenantId}/invitations`
### Authorization
Bearer token required.
## Description
Invites a user to join the tenant and assigns roles to them within the tenant's context.
If the user has not registered to the Self Service Portal yet, then they will receive an email with a link to accept the invite.
If the user has already registered, then they will be added as a member to the tenant immediately.
> **Note:** This endpoint can only be called by a Portal user that is a member of the tenant. Machine-to-machine (M2M) clients are not currently supported as inviters and will receive a `404 Resource Not Found` response, even when the client holds an `admin` role. This applies regardless of the role assigned, such as `admin`, `issuer`, or `dts-provider`.
>
> To invite members, use a Portal user that is a member of the target tenant. A reliable workflow is to create the tenant from the [MATTR Portal](/docs/platform-management/portal) rather than via an M2M client. The Portal user that creates the tenant becomes a member automatically, and can then invite additional users to it, either from the Portal UI or by calling this endpoint with the Portal user's access token.
### **Analytic events**
* TENANT_MEMBER_INVITATION_CREATE_START
* TENANT_MEMBER_INVITATION_CREATE_SUCCESS
* TENANT_MEMBER_INVITATION_CREATE_FAIL
### Path Parameters
- `tenantId`: string (required)
Identifier of the tenant to add the member to.
### Request Body
```json
{
"email": "john-doe@example.com",
"roles": [
"dts-provider",
"issuer"
]
}
```
### Responses
#### 200 - Member invited
```json
{
"userId": "8f6d40a9-d913-45e8-aa3e-8c99d62cd8fb",
"status": "Active",
"inviteExpiresAt": "2025-08-22T07:46:09.510Z"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 401 - Unauthorized. The client is not recognized by authorization server.
#### 403 - Forbidden. The client is recognized by authorization server but is not allowed to access this resource.
#### 404 - Not Found. The specified resource was not found.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 500 - Internal Server Error. An unexpected error occurred.
# Retrieve tenant members
## Endpoint
```
GET /v1/tenants/{tenantId}/members
```
Full URL: `https://manage.au01.mattr.global/v1/tenants/{tenantId}/members`
### Authorization
Bearer token required.
## Description
Retrieves a list of all users that have access to the tenant.
### **Analytic events**
* TENANT_MEMBER_RETRIEVE_LIST_START
* TENANT_MEMBER_RETRIEVE_LIST_SUCCESS
* TENANT_MEMBER_RETRIEVE_LIST_FAIL
### Path Parameters
- `tenantId`: string (required)
Identifier of the tenant to retrieve.
### Query Parameters
- `limit`: number (default: 100)
Range size of returned list.
- `cursor`: string
Starting point for the list of entries.
### Responses
#### 200 - Tenant's members retrieved
```json
{
"data": [
{
"id": "8f6d40a9-d913-45e8-aa3e-8c99d62cd8fb",
"email": "john-doe@example.com",
"name": "John Doe",
"status": "Active",
"roles": [
"dts-provider",
"issuer"
],
"permissions": [
"dids:read",
"dids:create"
],
"inviteExpiresAt": "2025-08-22T12:00:00.000Z"
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 401 - Unauthorized. The client is not recognized by authorization server.
#### 403 - Forbidden. The client is recognized by authorization server but is not allowed to access this resource.
#### 500 - Internal Server Error. An unexpected error occurred.
# Retrieve a tenant member
## Endpoint
```
GET /v1/tenants/{tenantId}/members/{userId}
```
Full URL: `https://manage.au01.mattr.global/v1/tenants/{tenantId}/members/{userId}`
### Authorization
Bearer token required.
## Description
Retrieves an existing user that has access to the tenant.
### **Analytic events**
* TENANT_MEMBER_RETRIEVE_START
* TENANT_MEMBER_RETRIEVE_SUCCESS
* TENANT_MEMBER_RETRIEVE_FAIL
### Path Parameters
- `tenantId`: string (required)
Identifier of the tenant.
- `userId`: string (required)
Identifier of the user.
### Responses
#### 200 - Tenant member retrieved
```json
{
"id": "8f6d40a9-d913-45e8-aa3e-8c99d62cd8fb",
"email": "john-doe@example.com",
"name": "John Doe",
"status": "Active",
"roles": [
"dts-provider",
"issuer"
],
"permissions": [
"dids:read",
"dids:create"
],
"inviteExpiresAt": "2025-08-22T12:00:00.000Z"
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 401 - Unauthorized. The client is not recognized by authorization server.
#### 403 - Forbidden. The client is recognized by authorization server but is not allowed to access this resource.
#### 500 - Internal Server Error. An unexpected error occurred.
# Delete a tenant membership
## Endpoint
```
DELETE /v1/tenants/{tenantId}/memberships/{userId}
```
Full URL: `https://manage.au01.mattr.global/v1/tenants/{tenantId}/memberships/{userId}`
### Authorization
Bearer token required.
## Description
Removes the membership of a user from the specified tenant. This will remove all user permissions for this tenant.
### **Analytic events**
* TENANT_MEMBERSHIP_DELETE_START
* TENANT_MEMBERSHIP_DELETE_SUCCESS
* TENANT_MEMBERSHIP_DELETE_FAIL
### Path Parameters
- `tenantId`: string (required)
Identifier of the tenant the user is being removed from.
- `userId`: string (required)
Identifier of the user being removed.
### Responses
#### 204 - User removed from tenant
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 401 - Unauthorized. The client is not recognized by authorization server.
#### 403 - Forbidden. The client is recognized by authorization server but is not allowed to access this resource.
#### 500 - Internal Server Error. An unexpected error occurred.
# Update a tenant membership
## Endpoint
```
PUT /v1/tenants/{tenantId}/memberships/{userId}
```
Full URL: `https://manage.au01.mattr.global/v1/tenants/{tenantId}/memberships/{userId}`
### Authorization
Bearer token required.
## Description
Updates the membership of a user in the tenant. This includes the roles assigned to this user for this tenant.
### **Analytic events**
* TENANT_MEMBERSHIP_UPDATE_START
* TENANT_MEMBERSHIP_UPDATE_SUCCESS
* TENANT_MEMBERSHIP_UPDATE_FAIL
### Path Parameters
- `tenantId`: string (required)
Identifier of the tenant.
- `userId`: string (required)
Identifier of the user who's membership is being updated.
### Request Body
```json
{
"roles": [
"dts-provider",
"issuer"
]
}
```
### Responses
#### 200 - Membership updated.
```json
{
"userId": "879a5524-d515-4aee-824a-c52fdcd4eea6",
"tenantId": "8f49b206-e0bb-474d-8a4d-62186a9de886",
"roles": [
"dts-provider",
"issuer"
]
}
```
#### 400 - Bad Request. The request was malformed or missing required parameters.
```json
{
"details": [
{
"msg": "Invalid value",
"param": "id",
"location": "body"
}
]
}
```
#### 401 - Unauthorized. The client is not recognized by authorization server.
#### 403 - Forbidden. The client is recognized by authorization server but is not allowed to access this resource.
#### 500 - Internal Server Error. An unexpected error occurred.