>_ Analyst Engineering

Which ISO 20022 Identifier Do You Trace On? MsgId, InstrId, EndToEndId, TxId, and UETR

Written by Ahmed at Analyst Engineering, a Senior Technical Business Analyst with 10+ years in banking and payments delivery.

Cover comparing the five ISO 20022 payment identifiers and which of them survives each hop in the payment chain.

Key takeaways

  • Five identifiers travel with a payment and only the UETR is designed to be globally unique and immutable across every hop, which makes it the only safe key for tracing a payment end to end.
  • EndToEndId is assigned by the originating customer, not by a bank, so it is unique only to the extent that customer made it unique, and the literal value NOTPROVIDED is legal and common: never use it as a database primary key.
  • MsgId identifies a message, not a payment, and changes at every hop, so deduplicating on MsgId catches retransmissions of the same message and misses the same payment resent inside a new one.
  • A status or return message points back with Original prefixed fields, so the identifier you choose to store determines which investigations you can answer later: store all five, key on the UETR.

Five identifiers travel with an ISO 20022 payment, and they are not interchangeable. MsgId identifies a message and changes every hop. InstrId is point to point. TxId belongs to the interbank chain. EndToEndId belongs to the customer and is unique only by luck. The UETR is the one identifier generated once and preserved by every party, which makes it the only safe key for tracing, deduplication, and investigation.

Ask five people on a payments programme what the difference is between the EndToEndId and the UETR and you will get three answers and two guesses. It matters more than it sounds, because these identifiers end up as database columns, reconciliation keys, and the thing an operations analyst types into a search box at two in the morning. Choosing the wrong one produces a system that works in testing, where every payment is unique and well behaved, and fails in production, where a corporate sends four hundred payments a day all carrying the reference NOTPROVIDED.

This is the reference I wish someone had handed me on my first migration. It sits underneath the payment message flows: the flow tells you which message goes where, and this tells you what stays the same as it does. The wider payments domain context is in Break Into Banking.

Where do the identifiers live in the message?

Two levels. The group header carries the identifier for the message as a whole. The payment identification block inside each transaction carries the identifiers for that payment.

<FIToFICstmrCdtTrf>
  <GrpHdr>
    <MsgId>BNKAGB2L-20260905-000417</MsgId>
    <CreDtTm>2026-09-05T09:14:22+02:00</CreDtTm>
    <NbOfTxs>1</NbOfTxs>
  </GrpHdr>
  <CdtTrfTxInf>
    <PmtId>
      <InstrId>INSTR-77120</InstrId>
      <EndToEndId>INV-2026-1188</EndToEndId>
      <TxId>TXN-BNKAGB2L-99031</TxId>
      <UETR>7f4c1a20-9e6b-4d31-8a55-2c9d10bb4e77</UETR>
    </PmtId>
    ...
  </CdtTrfTxInf>
</FIToFICstmrCdtTrf>

One message, one payment, five identifiers, five different owners. Read PmtId as a stack of references at different scopes rather than as a list of alternatives, because that is the mental model that makes the rest of this obvious.

What does each identifier actually mean?

IdentifierAssigned byScopeSurvives a hop?LengthUse it for
MsgIdThe sender of this messageOne message, all transactions in itNo, rewritten each hop35Detecting a resent message; technical acknowledgement
InstrIdThe instructing agentOne instruction, between two adjacent partiesNo, rewritten each hop35Point to point matching with your immediate counterparty
TxIdThe first instructing agentThe transaction within the interbank chainUsually, within the chain35Interbank transaction identity where no UETR is present
EndToEndIdThe originating customerThe customer’s own referenceYes, must be passed unchanged35Matching a payment to the customer’s invoice or order
UETRThe first instructing agentThe payment, globallyYes, immutable by rule36Tracing, deduplication, investigations, your primary key

The column that matters is “survives a hop”. Everything else follows from it.

MsgId is a message reference, not a payment reference. One message can carry a thousand transactions, and every intermediary generates a new one when it forwards. If you store MsgId as “the payment reference”, the value you hold is your counterparty’s envelope number for the batch you happened to receive, which is meaningless to anyone upstream of them.

InstrId is deliberately local. It exists so two adjacent parties can discuss one instruction without ambiguity, and the next agent in the chain will replace it with their own. It is the right key for matching your own outbound instruction to the acknowledgement your correspondent sends back, and the wrong key for anything beyond that pair.

TxId identifies the transaction inside the interbank leg. It is more durable than InstrId and less durable than the UETR, and schemes vary in whether they require it and whether intermediaries preserve it, which is exactly the sort of thing the usage guideline decides rather than the base standard.

EndToEndId is the customer’s reference, and this is the one that causes production incidents. It is assigned in the pain.001 by the corporate or retail customer, and the rule is that every party passes it through unchanged so the beneficiary can reconcile against their own invoice. That rule says nothing whatsoever about uniqueness.

UETR is the Unique End-to-end Transaction Reference: a UUID version 4, 36 characters including hyphens, generated once by the first instructing agent and immutable thereafter. It is the identifier the standard added specifically because the others could not do this job, and it is what makes cross-border tracking possible between institutions that share no other reference.

Why is EndToEndId dangerous as a key?

Because its uniqueness is somebody else’s problem, and that somebody is not obliged to solve it.

Three failure modes show up in real traffic. Collision across customers: two unrelated corporates both use INV-1001, and your unique index on end_to_end_id rejects a perfectly valid payment. Collision within a customer: an ERP that resets its counter, or a payment file resubmitted after a correction with the same references. And the one that surprises people, the literal placeholder: NOTPROVIDED is an accepted value when the initiating party supplies no reference, and it arrives in volume.

On one migration our warehouse load started failing intermittently in the third week of parallel running. The cause was a unique constraint on the end to end reference, added by a developer who had reasonably assumed that a field described in the specification as “end to end identification” identified something end to end. It does. It just does not identify it uniquely.

The correct treatment is to store EndToEndId as an indexed, non-unique attribute used for customer service and for the beneficiary’s reconciliation, and to key on the UETR. If you need a natural key for a payment inside your own systems before a UETR exists, generate your own and keep it separate, which is the same discipline as an idempotency key on an API.

How do status and return messages point back?

Every downstream message identifies what it is about by repeating the original identifiers with an Orgnl prefix. This is the mechanism that makes an exception flow traceable, and it determines which identifiers you must have stored.

<TxInfAndSts>
  <OrgnlInstrId>INSTR-77120</OrgnlInstrId>
  <OrgnlEndToEndId>INV-2026-1188</OrgnlEndToEndId>
  <OrgnlTxId>TXN-BNKAGB2L-99031</OrgnlTxId>
  <OrgnlUETR>7f4c1a20-9e6b-4d31-8a55-2c9d10bb4e77</OrgnlUETR>
  <TxSts>RJCT</TxSts>
  <StsRsnInf>
    <Rsn><Cd>AC01</Cd></Rsn>
  </StsRsnInf>
</TxInfAndSts>

Note what this implies for your data model. A pacs.002 arriving with OrgnlUETR can be matched to the original payment instantly and unambiguously. One arriving with only OrgnlInstrId can be matched only if you stored the InstrId you sent, which is why “we only keep the UETR” is as wrong as “we only keep the EndToEndId”. Store all five, index the UETR, and the matching logic for statuses, returns, and camt.056 cancellation requests is a single join instead of a fuzzy search on amount and date.

The matching query for an incoming status is then boring, which is the goal:

select p.payment_id, p.status
from payments p
join incoming_status s
  on  s.orgnl_uetr = p.uetr
 or  (s.orgnl_uetr is null and s.orgnl_tx_id = p.tx_id)
 or  (s.orgnl_uetr is null and s.orgnl_instr_id = p.instr_id);

The fallback branches exist because not every scheme and not every counterparty populates the UETR on every message type. Write the fallback, log which branch matched, and monitor the counts: a rising share of matches on the fallback branches is an early warning that a counterparty has changed behaviour.

Which identifier for which job?

This is the decision table worth pinning above a desk.

JobIdentifierWhy
Trace a payment across banksUETRThe only one every party preserves
Deduplicate incoming paymentsUETRGenerated once per payment, immutable
Detect a resent messageMsgIdIdentifies the envelope, which is what was resent
Match your outbound to a counterparty ackInstrIdPoint to point by design
Match a status or return to its paymentOrgnlUETR, then OrgnlTxId, then OrgnlInstrIdFollow the fallback chain
Answer “where is my payment” from a customerEndToEndId to find it, UETR to trace itThe customer knows only their own reference
Beneficiary reconciliationEndToEndId or structured creditor referenceIt is what reaches the creditor’s ledger
Primary key in your warehouseUETRUnique, stable, and present on every related message

The two-step in row six is the one operations teams need spelled out. A customer calls with their own reference, so you search on EndToEndId filtered by that customer, find the payment, and then trace on the UETR. Building the search screen to accept only one of them makes half the enquiries unanswerable, and building it to search EndToEndId globally returns other customers’ payments, which is a data protection incident rather than a usability problem.

What should you test?

Identifier handling is a rich source of defects precisely because it works fine with well behaved test data. Five test cases catch most of it, and they belong in the suite alongside your pacs.008 field tests.

UETR immutability. Send a payment through a full chain including your own forwarding logic, and assert the UETR is byte identical at every hop. The common defect is a system that regenerates rather than propagates, which breaks every downstream investigation while looking entirely healthy.

UETR format. Assert the value is a valid UUID version 4, lowercase, 36 characters. A payment with a well formed but non-compliant reference, an uppercase UUID or a 32 character version with the hyphens stripped, is a rejection at the scheme and a debugging session at your end.

Duplicate EndToEndId. Submit two genuinely different payments carrying the same EndToEndId and assert both are accepted and both are traceable. This is the test that catches the unique index.

NOTPROVIDED. Submit a payment with EndToEndId set to NOTPROVIDED and assert nothing downstream treats it as a real reference, especially the beneficiary advice and any reconciliation matching.

Status matching fallback. Send a pacs.002 without an OrgnlUETR and assert your matching falls back to OrgnlTxId correctly, and that a status matching nothing raises an exception rather than being silently discarded. Unmatched statuses that disappear into a log file are how payments end up stuck in an intermediate state with nobody aware, and they belong in the state machine as an explicit condition.

Building this properly is negative test design applied to identity, and the full method for deriving and structuring these suites is in API Testing and QA Mastery for BAs.

What does this mean for your data model?

Three rules, and they resolve most design arguments before they start.

Store every identifier you receive, including the ones you think are useless. Storage is cheap and an investigation eighteen months later is not. The InstrId you discarded is the only handle a correspondent has on a message they sent you.

Key on the UETR, index the rest. One unique constraint, on the UETR, and only where you are certain of your own domain: even the UETR can legitimately repeat in your database if you hold both the outbound and inbound legs of the same payment, so the unique key is usually UETR plus leg or plus direction, not UETR alone. That subtlety catches teams late.

Record which identifier matched. Every automated match writes down what it matched on. It costs a column and it turns “reconciliation is behaving oddly” into a query, which is exactly the instrumentation that makes reconciliation design maintainable rather than mysterious.

The takeaway

The five ISO 20022 payment identifiers operate at different scopes and only two are intended to cross the whole chain. MsgId identifies a message and is rewritten at every hop. InstrId is point to point between adjacent agents. TxId identifies the transaction within the interbank chain. EndToEndId is the customer’s own reference, passed through unchanged, and unique only to the extent the customer made it so, with NOTPROVIDED a legal and common value. The UETR is a UUID generated once and preserved by everyone, which is what makes it the tracing, deduplication, and primary key.

Store all five, key on the UETR plus the leg, index the others, follow the Orgnl prefixed fallback chain when matching statuses, and record which identifier matched so drift is visible. Then test immutability, format, duplicate customer references, the NOTPROVIDED literal, and the unmatched status path, because every one of those passes silently with clean test data and fails in production.

Start with Break Into Banking for the payments domain and API Testing and QA Mastery for BAs for the test discipline, or browse everything at The Tech BA Toolkit.

Ahmed is a Senior Technical Business Analyst with 10+ years in banking and payments. He builds practical guides and tools for analysts at The Tech BA Toolkit.

Tags: ISO 20022, Payments, Systems Analysis, Data Modeling, Banking

About the author

Analyst Engineering is written by Ahmed, a Senior Technical Business Analyst with 10+ years of banking and payments delivery experience: ISO 20022 and SWIFT messaging, payments API integration, Kafka event validation, and production support. Every article comes from real delivery work, and each one is reviewed and updated as tools and standards change.

Newsletter

Subscribe

Practical, no-fluff playbooks for technical analysts who analyze, code, test, and support. New articles straight to your inbox.

No spam. Unsubscribe anytime.