Monitoring the OID4VCI Pre-authorized Code flow
Learn which analytic events the OID4VCI Pre-authorized Code flow emits at each step, which identifiers correlate them, and which parts of the flow MATTR VII cannot observe.
This page maps the OID4VCI Pre-authorized Code flow onto the analytic events MATTR VII emits, so you can tell at a glance how far an issuance attempt progressed.
It assumes you are familiar with how to monitor a flow, which covers the event structure, the identifiers, and the general method for investigating a failure. This page covers only what is specific to this flow.
What you can observe
An OID4VCI issuance is not a single request. It is a sequence of separate exchanges, and only some of them reach MATTR VII.
| Step | Reaches MATTR VII | Emits events |
|---|---|---|
| 1. Offer creation by your backend | Yes | Yes |
2. Wallet fetches issuer metadata from the .well-known endpoints | Yes | No |
| 3. Wallet reads the offer and extracts the pre-authorized code | No | No |
| 4. Token exchange at the token endpoint | Yes | Yes |
| 5. Credential request at the credential endpoint | Yes | Yes |
Step 3 is invisible because the credential offer URI carries the offer inside a credential_offer
query parameter. The wallet parses it locally, so there is no request to MATTR VII. Step 2 does reach
MATTR VII, but the .well-known endpoints serve public metadata and do not emit analytic events.
Because steps 2 and 3 emit nothing, you cannot use analytic events to distinguish "the holder never opened the offer" from "the holder opened the offer and their wallet failed before contacting MATTR VII". If you need to observe that gap, instrument it in the channel you use to deliver the offer.
Workflow and the events it emits
The following diagram extends the Pre-authorized Code workflow with the events emitted at each hop:
An issuance emits other events alongside these, recording work MATTR VII performs on your behalf. Refer to the Events registry for the full catalog.
The events that matter
These are the events that tell you something about the journey.
| Event | Step | What it tells you | Identifiers in data |
|---|---|---|---|
OPENID_PRE_AUTHORIZED_OFFER_CREATE_START | 1 | Your backend asked for an offer | sessionId, userId |
OPENID_PRE_AUTHORIZED_OFFER_CREATE_SUCCESS | 1 | The offer exists and is claimable | sessionId, offerId, userId, expiresAt |
OPENID_TOKEN_START | 4 | A wallet presented a pre-authorized code that matched a live offer | offerId, userId, client_id |
OPENID_TOKEN_SUCCESS | 4 | The wallet holds an access token | sessionId, userId, expires_in, scope |
OPENID_CREDENTIAL_START | 5 | The wallet asked for the credential | sessionId, userId, clientId, format |
OPENID_CLAIM_SOURCE_RETRIEVE_START | 5 | MATTR VII called your claims source | sessionId, claimSourceId, userId |
OPENID_CLAIM_SOURCE_RETRIEVE_SUCCESS | 5 | Your claims source returned usable claims | sessionId, claimSourceId, claimSource.url |
USER_CREDENTIAL_CREATE_SUCCESS | 5 | The credential record exists | sessionId, offerId, userId, credentialId, credentialConfigurationId |
OPENID_CREDENTIAL_SUCCESS | 5 | The credential was issued and returned to the wallet | sessionId, credentialOfferId, credentialConfigurationId, credentialId, userId |
Two of these are worth singling out:
USER_CREDENTIAL_CREATE_SUCCESScarriesofferId,sessionId,userId,credentialId, andcredentialConfigurationIdtogether, which makes it the best place to join a MATTR VII issuance to a record in your own system.OPENID_CREDENTIAL_SUCCESSis the event that closes the loop on an offer, because it carriescredentialOfferIdalongside the issuedcredentialId.
Fields removed before storage
Some fields are removed from an event before it is stored, so an event may return less than the operation handled. Claims are the main example: you can confirm that your claims source was called and see the URL used, but you cannot read the claims it returned. Refer to Sanitized events.
Failure events
Each of these families follows the START, SUCCESS, FAIL pattern, and the FAIL variants carry
data.error.type and data.error.message. The ones worth looking for are:
| Event | Use it to find |
|---|---|
OPENID_PRE_AUTHORIZED_OFFER_CREATE_FAIL | An offer that was never created, for example one referencing an unknown credential configuration |
OPENID_TOKEN_FAIL | A rejected token exchange, most often a wrong transaction code |
OPENID_CREDENTIAL_FAIL | A rejected credential request, including claim mapping problems |
OPENID_CLAIM_SOURCE_RETRIEVE_FAIL | Your claims source being unreachable or returning something unusable |
MOBILE_CREDENTIAL_SIGN_FAIL | A certificate or signing problem, which is where the specific reason appears |
WEBHOOK_EVENT_PROCESS_FAIL | A webhook that MATTR VII could not deliver |
Identifiers in this flow
The identifiers common to every flow
apply here. This flow adds the following, all of which live inside the event data and therefore
cannot be filtered on directly:
| Identifier | What it identifies | Where you first get it |
|---|---|---|
offerId and credentialOfferId | One credential offer, through to issuance | The id in the offer creation response |
userId | The MATTR VII user record | The offer creation response |
credentialId | The issued credential | USER_CREDENTIAL_CREATE_SUCCESS, or a webhook |
credentialConfigurationId | The configuration the credential was issued against | Your credential configuration |
claimSourceId | The claims source MATTR VII called | Your claims source configuration |
A single issuance spans three separate exchanges, so it has three different request IDs. The
sessionId is what ties them together.
Filtering by clientIds will not find the token or credential events for an issuance. The token
and credential endpoints are called by the wallet, and their events carry no top-level client ID,
so a clientIds filter returns your offer creation events and nothing that followed them. The
wallet's OAuth client is still recorded as data.clientId on OPENID_CREDENTIAL_START and
OPENID_CREDENTIAL_SUCCESS.
Confirming issuance without polling
Rather than polling the Events API to find out whether a credential was issued, subscribe to a
webhook. The OpenIdCredentialIssued and
OpenIdCredentialIssuedSummary event types both cover this flow, and their payload carries
credentialOfferId, credentialId, userId, credentialConfigurationId, and clientId.
There is no webhook for a failed issuance. Webhooks tell you that issuance succeeded. They cannot tell you that it failed.
Next steps
How would you rate this page?
Last updated on