How to monitor a flow
Learn how MATTR VII analytic events map onto a multi-step flow, how to correlate them, and how to work from a symptom back to the event that explains it.
MATTR VII emits analytic events as it works. This page describes how to read those events as a flow rather than as a list, so you can tell how far an attempt progressed and, when something goes wrong, where it stopped.
The approach here applies to any MATTR VII flow. Individual flow guides, such as Monitoring the OID4VCI Pre-authorized Code flow, build on it by showing which events that specific flow emits and when.
How events map to a flow
Two patterns are worth internalizing before you write any monitoring code.
Every operation emits a pair. An operation emits a START event when it begins, then either a
SUCCESS or a FAIL event when it finishes. A START with no matching SUCCESS or FAIL means
the operation did not complete. Refer to Event types.
A flow is several operations across several calls. A user-facing flow is rarely a single HTTP
request. It is usually a sequence of separate exchanges, some made by your backend and some made by a
wallet or browser you do not control. Each exchange has its own requestId, and the events within it
share that value. The sessionId is what links the exchanges to each other.
That gives you two levels of grouping:
- Group by
requestIdto see one exchange end to end. - Group by
sessionIdto see the whole journey.
Refer to Correlating events for the full description of each identifier.
Not every step in a flow reaches MATTR VII. Steps that happen between a holder and their wallet, or between your backend and your own systems, are invisible to analytic events. Each flow guide identifies which steps those are.
Trace a flow with the identifiers you have
Where you start depends on what you are holding when the question arrives.
| You have | Do this |
|---|---|
| A request ID | Query GET /v1/events?requestIds={id}. This returns every event for that one exchange. |
| An event ID | Query GET /v1/events?ids={id}, then use the requestId on the result to widen to the full exchange. |
| A session ID | Query by event types over the relevant time window, then match on data.sessionId in your own code. |
| A resource ID, such as an offer or credential ID | Query by the event types that carry it over the relevant time window, then match on the field in data. |
| A time window only | Query by the event types that mark each step of the flow, then read data to narrow down. |
| A user complaint only | Establish roughly when they tried, to within a few minutes, then use the time window approach. |
Only ids, requestIds, categories, types, clientIds, managementUserIds, and the date range
are filterable. Anything inside data has to be matched on your side. Refer to
How to query analytic events for the query parameters
and the behavior to expect.
Investigate a failure
Work backwards from the last thing that happened.
- Log in to the MATTR Portal and select Monitoring under Platform Management.
- Select the tenant you are investigating from the drop-down list in the upper-left corner.
- Narrow the list using the Event / Request ID box, the Events drop-down list to pick a category or specific types, and the Time drop-down list to set the window.
- Open an event to see its detail. The event detail shows the other events that share the same request ID, which is the quickest way to see one exchange end to end.
- Read the full
datapayload on the event detail.
A partial or misspelled identifier returns an empty result rather than an error. An empty result means the query matched nothing, not that no events exist.
Then read the failure. A FAIL event carries the reason in data.error:
{
"error": {
"type": "invalid_tx_code",
"message": "Transaction code is invalid"
}
}error.type: The error classification. Anerror.typeofunknownmeans the failure came from an unhandled exception rather than a recognized condition, and the message will not be specific either.error.message: The description.
Look for the deepest error
When one operation fails because a nested operation failed, the outer event often reports a general
failure while the inner event carries the specific reason. Check for other FAIL events on the same
requestId before you conclude that a cause is unknown. The most useful error is usually the one
deepest in the request.
When there is no event
The absence of an event is evidence, but it is not proof that nothing happened.
Some requests are rejected before MATTR VII begins recording them, typically where input is malformed, expired, or already used. Those return an HTTP error to the caller and produce no analytic event. Steps that never reach MATTR VII at all, such as a wallet parsing something locally, likewise produce nothing.
So when you find nothing, do not conclude the attempt never happened. Instead, work from the last event that did fire:
- Identify the last successful step. That tells you which exchange the flow reached.
- Consider what could reject the next step before it is recorded. Expiry, reuse, and a value that does not resolve are the usual candidates.
- Compare timings. If the last
SUCCESSevent carries an expiry, comparing it against when the user says they tried often settles the question on its own. - Confirm the tenant and the window. Check you are querying the tenant the flow ran against, and that your time window is in UTC.
The flow guides call out the specific inferences worth making for each flow.
What to record on your side
Analytic events describe what MATTR VII saw. They cannot describe what happened in your own systems or in the channel you use to reach a user. Capturing a small amount at the time makes an attempt traceable later:
- The
x-request-idresponse header on every MATTR VII call your backend makes. This is the single most useful thing to log. - The identifiers MATTR VII returns to you, such as the
idof a resource you created. - Your own reference for the user, and the time you contacted them. Nothing in MATTR VII records this.
- The timestamp, in UTC, of any failure your backend sees.
Confirm success without polling
Do not poll the Events API to find out whether an operation completed. Subscribe to a webhook instead and let MATTR VII tell you.
Webhooks report that something succeeded. They do not report failures, so a webhook that never arrives tells you nothing about why. Use webhooks to confirm success, and analytic events to investigate everything else.
Worked example
A Pre-authorized Code issuance is a good illustration, because it spans three separate exchanges: your backend creates the offer, the wallet exchanges the pre-authorized code for an access token, and the wallet requests the credential. That is three request IDs and one session ID.
If a user reports that they never received their credential, the last event that fired tells you which exchange to look at:
- Offer creation succeeded but nothing followed. The wallet never completed a token exchange, so the problem lies between creating the offer and the wallet presenting it.
- The token exchange succeeded but no credential event followed. The wallet obtained a token and then stopped, or its credential request was rejected before it was recorded.
- A credential event failed. The reason is in
data.error, and there may be a more specificFAILevent on the same request ID.
Refer to Monitoring the OID4VCI Pre-authorized Code flow for the events this flow emits, and to Troubleshooting the OID4VCI Pre-authorized Code flow for the symptoms specific to it.
What to send us if you are stuck
If you cannot resolve it, contact our support team and include:
- The request ID from the
x-request-idresponse header, or from the event detail. - The exact timestamp in UTC, to the second where you can.
- The tenant the attempt was made against.
- The event ID of the most relevant event, if you found one.
A request ID or a precise timestamp lets us locate your exact case. Without either, we are searching a much wider window.
Next steps
How would you rate this page?
Last updated on