light-mode-image
Learn
Revocation

Integrating mDocs revocation into your workflow

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

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 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

By default, signed mDocs are not revocable. 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:

Request
POST /v2/credentials/mobile/configurations
Request body
{
  "type": "org.iso.18013.5.1.mDL",
  "includeStatus": true, 
  "claimMappings": {
    // Your claim mappings
  }
  // Other configuration fields
}

See the API reference for the full request schema.

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 that notifies your backend when a credential is issued.

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:

Request
POST /v1/webhooks
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, Webhooks guide, and Webhooks tutorial.

Handle the webhook payload

When a credential is issued, MATTR VII sends a webhook notification with the following structure:

OpenIdCredentialIssuedSummary payload
{
  "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": { 
    "externalUserId": "student-12345"
  } 
}

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

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

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)

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.

Request
POST /v1/openid/offers/pre-authorized
Request body
{
  "credentials": [
    "946c4d4a-289b-4d14-8082-41b6bf749c35"
  ],
  "claims": {},
  "expiresIn": {
    "minutes": 5,
    "seconds": 0
  }
}
Response
{
  "id": "8241400f-de3b-42c5-ad7c-8a380039e796", 
  "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.

Webhook payload
{
  "credentialOfferId": "8241400f-de3b-42c5-ad7c-8a380039e796", 
  "credentialId": "9613ac5e-a0ba-4512-ba0b-90e91b2744bc", 
  "userId": "19bf8183-a9dc-41cd-9336-1f5d19f1ae3d",
  "userClaims": {}
}
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 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:

Request
POST /v1/users
Request body
{
  "claims": {
    "externalUserId": "employee-12345"
  }
}
Response
{
  "id": "19bf8183-a9dc-41cd-9336-1f5d19f1ae3d", 
  "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:

Request
POST /v1/openid/offers/pre-authorized
Request body
{
  "credentials": [
    "946c4d4a-289b-4d14-8082-41b6bf749c35"
  ],
  "userId": "19bf8183-a9dc-41cd-9336-1f5d19f1ae3d", 
  "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.

Webhook payload
{
  "credentialOfferId": "8241400f-de3b-42c5-ad7c-8a380039e796",
  "credentialId": "9613ac5e-a0ba-4512-ba0b-90e91b2744bc", 
  "userId": "19bf8183-a9dc-41cd-9336-1f5d19f1ae3d",
  "userClaims": { 
    "externalUserId": "employee-12345"
  } 
}

You can also retrieve all credentials issued to a specific user via the Retrieve all user credentials data endpoint, which returns credential metadata including the credentialId, status, and offerId.

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

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 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

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 to include relevant user identifiers:

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:

Request
GET /v1/users/{userId}
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.

Webhook payload
{
  "credentialId": "9613ac5e-a0ba-4512-ba0b-90e91b2744bc", 
  "userId": "7382276d-ef75-4d17-8fb0-1d3aec4647ab",
  "credentialConfigurationId": "1d8c7ad7-84ce-4519-8365-7af986e4ee0e"
}

Correlation strategy summary

Issuance flowCorrelation methodWhat it associatesWhen to use
Pre-Authorized CodeMatch credentialOfferId from webhook to stored offer IDDatabase record (permit, license, enrollment)Each credential represents a specific record that can change independently of the user
Pre-Authorized CodeMatch userClaims.externalUserId from webhook to user in your systemSpecific userEach credential is tied to a user; revoke when user status changes (termination, expiration)
Authorization CodeLook up userId, retrieve authenticationProvider.subjectId, match to user in your systemSpecific userUser authenticates to claim; credentials tied to authenticated user identity
Authorization CodeUse custom claims or interaction hooks to capture database record identifierDatabase record (via custom integration)Advanced: Credential represents a record, requires additional integration with authentication flow
BothQuery user credentials endpoint to list all credentials for a userSpecific userYou want to find all credentials associated with a specific user

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:

Request
POST /v2/credentials/mobile/{credentialId}/status
Request body
{
  "status": "invalid"
}
  • credentialId: Replace with the credential identifier you captured from the webhook or the user credentials endpoint.
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

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:

GO Hold Revoked Credential GO Hold Revocation Status

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:

GO Verify Revoked Credential

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

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:

SettingDefaultMinimumMaximum
TTL1 dayN/ACannot exceed EXP
EXP1 weekN/ACannot exceed the IACA validity

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

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

You can adjust these values using a Status list configuration. 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:

Request
POST /v2/credentials/mobile/status-lists/configurations
Request body
{
  "docType": "org.iso.18013.5.1.mDL",
  "timeToLiveDuration": {
    "hours": 2
  },
  "expiryDuration": {
    "hours": 12
  }
}

To update an existing configuration:

Request
PUT /v2/credentials/mobile/status-lists/configurations/{id}
Request body
{
  "timeToLiveDuration": {
    "hours": 2
  },
  "expiryDuration": {
    "hours": 12
  }
}

Propagation scenarios

The following scenarios illustrate how different TTL and EXP configurations affect when revocation becomes visible.

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)

  • 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

  • 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.

How would you rate this page?

Last updated on

On this page