Update via pre-authorized offer
Credential update is offered as an open beta preview feature and is not generally available yet. It is available on all tenants, and its behavior may change in a way that breaks existing integrations before it becomes generally available.
Overview
Your backend creates a pre-authorized credential offer that targets an existing credential bundle. When the wallet claims that offer, MATTR VII issues the new credential into that bundle instead of creating a new one, and the wallet replaces the copy the holder already has. See Replacing the credential in the wallet.
The holder claims the update the same way they claimed the credential in the first place, so no new authentication or identity check is involved. The bundle also keeps its credentialBundleId and credentialReferenceId, so lookups, event correlation, and any reference you hold against the bundle in your own systems stay valid.
Deciding when to update
Creating an offer against a bundle does not compare the credential you are about to issue against the one the holder already has, so nothing is suppressed when the offer is created. The decision is yours to make. Your own system of record or backend decides whether the data has actually changed, or whether a credential is close enough to expiry to warrant a fresh MSO, and you create the offer only then.
Obtaining an access token and requesting a credential are separate steps. The credential request has its own conditional mode, where the wallet supplies the version of the credential it currently holds and MATTR VII issues only if that version is stale. That mode depends on what the wallet sends, not on how the access token was obtained, so it applies to a pre-authorized update as well. If a wallet supplies a version that MATTR VII still considers current, the credential request returns 204 No Content, no credential is issued, and your update never reaches the holder.
An update delivered through a bundle-targeted offer is issuer-initiated, so your backend has already established that an update is warranted. A wallet claiming one of these offers should request the credential unconditionally rather than supplying a held version.
How it works
- Your backend determines that the credentials in a bundle need to be updated, using a
credentialBundleIdyou already have on hand. See Get a credentialBundleId. - Your backend creates a pre-authorized credential offer, passing that identifier as
credentialBundleId. MATTR VII validates the offer against the bundle before creating it, checking that the bundle exists and is active, that it belongs to the right user, and that the offer's credential configuration matches the bundle's credential type. See Requirements for the full list of checks. - You share the resulting offer URI with the holder, the same way you share any other pre-authorized credential offer.
- The wallet claims the offer and MATTR VII issues the new credential into that same bundle. If the bundle is bound to a wallet instance, only that wallet instance can claim the offer. The wallet then replaces the credential it already holds for that bundle, so the holder ends up with a single, current credential.
Replacing the credential in the wallet
MATTR VII issues the new credential into the existing bundle, so your backend keeps a single record to track. Removing the copy the holder already has is the wallet's responsibility.
The Holder SDK does not do this for you in this release. It claims a bundle-targeted offer the same way it claims any other offer, so unless your wallet app intervenes the holder is left holding both the old credential and the new one.
Compare the credentialReferenceId on the newly claimed credential against the credentials already in the wallet, and delete the earlier copy once the new one is stored. Comparing on credentialReferenceId rather than credentialBundleId also covers the cases where a holder claims another copy of the same credential without a bundle identifier, or re-authenticates through the Authorization Code flow. It does not cover a credential that is issued again for a claim set after first being issued without one, because the two have different references. See Add a claim set to a credential issued without one.
Requirements
Credential update is an open beta preview feature and is available on all tenants. You do not need to request access.
You can update a credential no matter which flow first issued it. A bundle created through the Authorization Code flow can be targeted by a pre-authorized update offer in the same way as one created through a pre-authorized offer.
MATTR VII validates the offer against the bundle before creating it. The target bundle must be in the active state. A bundle that has been unlinked from the wallet reports a state of unlinked and cannot be targeted for an update. If you supply userId, it must match the user the bundle belongs to. When the offer identifies the bundle only, without credentials, the holder must already have claimed the credential originally issued into the bundle.
When the offer is claimed, MATTR VII checks the claiming wallet against the wallet instance the bundle is bound to. A bundle is bound to a wallet instance when the wallet uses Wallet Attestation at first issuance. If you plan to update a credential, we recommend issuing it with Wallet Attestation so that only the wallet instance holding it can claim the update.
Creating the offer
Creating an update offer takes two steps: get the bundle's credentialBundleId, then create the offer against it.
Get a credentialBundleId
Which path applies depends on what your integration already has on hand.
-
You persisted
credentialBundleIdat initial issuance: The Holder SDK returnscredentialBundleIdto the wallet app alongside the credential when it is claimed. See Credential reference and bundle identifiers. If your wallet app relayed that value to your backend and you persisted it against your own user or credential records, you already have what you need. Skip ahead to Send the request. -
You only have the user's
userId: Resolve the bundle through the API. List the user's credential references, then list the bundles for the reference you want to update:GET /v1/users/{userId}/credential-referencesGET /v1/users/credential-references/{credentialReferenceId}/credential-bundlesEach bundle in the response includes its
credentialBundleId.If the credential was issued against a claim set identifier, each bundle also carries the
claimSetIdit was issued for, so you can pick out the bundle holding the record you need to update. You can also search bundles across the tenant by claim set. See Find credentials by claim set.
Send the request
Create the offer using the same credential offer endpoint you use for a first issuance, and identify the bundle to update with credentialBundleId:
POST /v1/openid/offers/pre-authorizedYou can structure the request in one of two ways:
- Identify the bundle only: Provide
credentialBundleId. MATTR VII infers the credential configuration from the bundle, and the claim set if the bundle was issued for one. A bundle has a credential configuration recorded against it once the holder has claimed the credential originally issued into it. - Name the credential configuration: Provide
credentialBundleIdtogether withcredentials.
{
"credentialBundleId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"claims": {
"givenName": "John",
"familyName": "Doe"
}
}{
"credentials": ["707e920a-f342-443b-ae24-6946b7b5033e"],
"credentialBundleId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"claims": {
"givenName": "John",
"familyName": "Doe"
}
}credentialBundleId: The bundle to update. See Get a credentialBundleId above.credentials(optional) : Names exactly one credential configuration, resolving to the same credential type as the bundle. Use the array form for a credential issued without a claim set, and the map form for a credential issued for a claim set. See Update a credential issued for a claim set.userId: Optional here. When omitted, the offer is automatically scoped to theuserIdassociated with the bundle. When provided, it must match thatuserId.
Every other field behaves as it does for a first issuance, including transactionCodeConfiguration, claims, claimsToPersist, and expiresIn. See Create a credential offer for the full field reference. If you want the update to happen silently, omit transactionCodeConfiguration: a transaction code must be entered by the holder during claiming, which defeats a silent update.
credentialBundleId is what makes the offer an update. Without it, the same request issues a new credential into a new bundle, and the wallet ends up holding an additional copy instead of a replacement.
Update a credential issued for a claim set
If the credential was issued for a claim set identifier, the update applies to exactly the claim set the bundle was issued for. Either identify the bundle only, so MATTR VII uses the bundle's claim set, or use the map form of credentials and name that same claim set. An offer that uses the array form, or names a different claim set, is not a valid update for that bundle.
When a user holds several credentials of the same type, such as one birth certificate per child, an update relies on two identifiers, and each does a different job:
credentialBundleIdsays which copy to replace: one credential in one wallet.- The claim set identifier says which record the credential represents. MATTR VII uses it to resolve that record's claims, including from your Claims source, and it is part of the credential's credential reference, so the updated credential stays linked to the same record.
Identify the bundle only. MATTR VII reads the claim set from the bundle. Provide the updated claims in claims. They are treated as the claim set's own claim record rather than flat user claims, the same as supplying them for that claim set in claimSets, and any claims listed in claimsToPersist are persisted against that claim set. For example, to update Sam's birth certificate after a name correction:
{
"credentialBundleId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"claims": {
"givenName": "Samantha"
}
}Name the claim set. Use the map form of credentials with the claim set the bundle was issued for, and provide the updated claims for it in claimSets. Each bundle carries the claimSetId it was issued for. If you already hold the credentialBundleId, retrieve the bundle and read claimSetId from the response:
GET /v1/users/credential-bundles/{credentialBundleId}If you are resolving the bundle from the user instead, the bundles listed for a credential reference carry claimSetId too. See Get a credentialBundleId. For example, to make the same update to Sam's birth certificate (claim set BC-1002):
{
"credentials": {
"707e920a-f342-443b-ae24-6946b7b5033e": ["BC-1002"]
},
"credentialBundleId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"claimSets": {
"707e920a-f342-443b-ae24-6946b7b5033e": {
"BC-1002": {
"givenName": "Samantha"
}
}
}
}With either structure, your Claims source can supply the credential's claims instead of the offer. When the wallet claims the update, your Claims source receives the claim set identifier through its claimSetId mapping, the same way it does at first issuance.
One offer updates one bundle. To update several credentials, create one offer for each.
Add a claim set to a credential issued without one
A bundle keeps the claim set it was issued for, so you cannot add a claim set identifier to a credential that was issued without one. An update offer that names a claim set against such a bundle is rejected with 400 Bad Request. This applies to any credential issued before you adopted claim sets.
To move a holder onto claim sets, issue the credential again for the claim set as a new credential. Create the offer without credentialBundleId, or have the holder claim it through the Authorization Code flow. When the wallet claims it, MATTR VII creates a new bundle. The claim set identifier is part of the credential reference, so the new credential also gets a new credentialReferenceId, and nothing links it to the credential the holder already has.
For example, Alex already holds a birth certificate for Sam that was issued before you used claim sets. You issue Sam's certificate again for claim set BC-1002. Alex's wallet now holds two copies of Sam's certificate, with different bundles and different credential references.
Plan for the earlier copy on both sides:
- In the wallet: Comparing
credentialReferenceIdorcredentialBundleIddoes not find the earlier copy. Your wallet app needs its own rule for removing it once the new credential is stored. - For verifiers: The earlier credential is not revoked automatically, and it remains valid until it expires. If it must stop being accepted, revoke it explicitly. This requires the credential to have been issued as revocable.
Offer response
The response is an ordinary credential offer response containing id, uri, and expiresAt. Share the uri with the holder exactly as you would for a first issuance. If the offer includes transactionCodeConfiguration, the response also carries a transactionCode to share with the holder through a separate channel.
{
"id": "2bc43e59-8e5d-41e6-8012-7d0399bfd07d",
"uri": "openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Ftenant.vii.mattr.global%22%2C%22credentials%22%3A%5B%2282477daf-be88-48fb-b2ec-249bae758438%22%5D%2C%22credential_configuration_ids%22%3A%5B%2282477daf-be88-48fb-b2ec-249bae758438%22%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22f5xMUX6p7fxThfpayJkdiMcTDtPFHda0kp3_AzP5xaw%22%7D%7D%7D",
"userId": "3259254f-f65c-49a4-b094-ae9b89f3d55a",
"expiresAt": "2026-09-08T04:52:22.165Z"
}How would you rate this page?
Last updated on