Update via pre-authorized offer
Credential update is offered as a closed beta preview feature and is not generally available yet. It is enabled per tenant, and its behavior may change in a way that breaks existing integrations before it becomes generally available. If you are interested in trying this feature, please contact us to have it enabled on your tenant.
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.
Requirements
Your tenant must be enabled for credential update. It is a closed beta preview feature, so contact us to have it enabled on your tenant. Until it is enabled, credentialBundleId is ignored and the offer issues a new credential bundle instead of updating the existing one.
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 is claimed, MATTR VII checks the claiming wallet against the wallet instance the bundle is bound to. A bundle is only bound to a wallet instance if the wallet used Wallet Attestation at first issuance. Wallet Attestation is optional, so a bundle with no bound wallet instance can be updated by any wallet that holds the offer.
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 Claiming a credential. 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, with credentialBundleId added:
POST /v1/openid/offers/pre-authorized{
"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: Must contain exactly one credential configuration identifier, resolving to the same credential type as the bundle.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.
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