All entries
Chapter VIII
Journal · 13 Nov 2026 · 10 min read

UETR vs EndToEndId vs TxId: ISO 20022 IDs

UETR vs EndToEndId vs TxId vs InstrId in ISO 20022: who sets each ID, which survive the chain, per-rail rules, returns, recalls and reconciliation keys.

A pacs.008 carries up to six identifiers for one payment, and only one of them is guaranteed to mean the same thing at every bank in the chain. The European Central Bank said so in a T2 Q&A: the UETR "is the only true, meaningful end-to-end ID", the others are point to point, and the EndToEndId "could contain NOTPROVIDED".

Most reconciliation bugs I have debugged on bank rails come from picking the wrong one as a key. This guide covers what each identifier is, who is allowed to set or change it, which rails require which, what happens to them in returns and recalls, and which ones actually come back in your statement files.

What Is a UETR in ISO 20022 Payments?

The Unique End-to-end Transaction Reference is a UUID version 4, fixed at 36 characters, carried in CdtTrfTxInf/PmtId/UETR. ISO defines it as a "universally unique identifier to provide an end-to-end reference of a payment transaction". The schema type is UUIDv4Identifier, and the pattern, printed in the EPC's 2025 SCT implementation guidelines and the Payments Canada Lynx guidelines alike, is:

[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}

Read the pattern literally and three things follow. Uppercase hex fails. A UUID v7 fails, because the version nibble must be 4. A nil UUID fails. JPMorgan's sample value 00000000-0000-4000-8000-000000000000 is the minimum shape that passes.

It predates ISO 20022 on Swift. From 18 November 2018 every Swift user, gpi member or not, had to put a UETR in field 121 of header block 3 on MT 103, 103 STP, 103 REMIT, 202, 205, 202 COV and 205 COV, or receive a NAK. The ordering institution generates it. Swift's rules say intermediaries "must not generate a new UETR", and the UETR of a cover payment "must be the same as the UETR of the underlying customer credit transfer".

Corporates can sometimes supply their own. pain.001.001.09 has an optional PmtId/UETR. Nordea's guide says it will create one if you leave it out, and Payments Canada's says "if provided, the bank passes this value on". Zürcher Kantonalbank's CBPR+ pain.001 handbook goes further and marks it mandatory.

UETR vs EndToEndId vs TxId vs InstrId: What Is the Difference?

Everything except the UETR is Max35Text. The ISO definitions tell you the intended scope, and the scope is the whole point.

IDPath in pacs.008Set byScopeCan it change along the chain?
MsgIdGrpHdr/MsgIdSender of each messageOne hopYes, new at every hop
InstrIdPmtId/InstrIdEach instructing partyOne hopYes, new at every hop
EndToEndIdPmtId/EndToEndIdThe initiating customerCustomer to customerNo, "passed on, unchanged, throughout the entire end-to-end chain"
TxIdPmtId/TxIdFirst instructing agent (the debtor's bank)Interbank chainNo, "unchanged throughout the entire interbank chain"
UETRPmtId/UETRDebtor agent, or the customer where allowedEnd to end, across networksNo
ClrSysRefPmtId/ClrSysRefThe clearing systemClearing system to memberAssigned once by the system

Three practical caveats sit behind that tidy table.

EndToEndId is the customer's reference, and the customer often has none. MT 103 has no end-to-end field, so JPMorgan's MT-to-MX mapping guide shows a translated payment carrying NOTPROVIDED. SEPA has the same rule in writing: "In the event that no reference was given, 'NOTPROVIDED' must be used". A unique index on EndToEndId will fall over on the second such payment. InstrId is shorter than its type suggests. CBPR+ caps it at 16 characters so that it can still translate to MT field 20, and Lynx applies the same limit "to align with CBPR+". If you generate 35-character instruction IDs because the XSD allows it, a cross-border route will reject them. TxId is a bank's ID, not yours. In SEPA it must be "meaningful to the Originator's PSP" and unique over time. You do not choose it when you submit a pain.001. You find it out afterwards, if your bank reports it.

In a pain.001 there is one more level: PmtInf/PmtInfId identifies the batch, above the per-transaction InstrId and EndToEndId. I covered the message families themselves in the pain.001 and pacs.008 guide.

Is UETR Mandatory? Swift, Fedwire, FedNow, SEPA and T2 Rules in 2026

It depends on the rail, and the rails disagree more than you would expect.

RailUETROther mandatory IDsDetail worth knowing
Swift CBPR+Mandatory in pacs.008 and pacs.009InstrId (16 chars), EndToEndIdMT coexistence ended 22 November 2025
Fedwire FundsMandatory in pacs.008, pacs.009, pacs.004EndToEndIdISO 20022 since 14 July 2025; IMAD and OMAD still exist
T2 (Eurosystem RTGS)Mandatory in pacs.008, pacs.009, pacs.010T2 generates one for screen-entered orders
Lynx (Canada)Mandatory in pacs.008InstrId (16 chars)
SEPA SCT and SCT InstOptional (0..1), no usage ruleTxId, EndToEndId2025 rulebooks, effective 5 October 2025
FedNowEither UETR or TxIdEndToEndIdPer Lead Bank's API docs; check your own provider
RTP (The Clearing House)OptionalEndToEndId, TxIdSame caveat
UK Faster PaymentsNoneISO 8583 based; planned NPA replacement mandates UETR
ACHNone15-digit trace numberFirst 8 digits are the ODFI routing number

The Fedwire row hides the sharpest edge. The Fed's format FAQ says the service "checks the UETR for proper format but does not validate the uniqueness of it". Swift does the opposite. Nordea's guide notes that Swift "performs validations to detect recycled UETRs", and Deutsche Bank reported during Transaction Manager testing that MT 191 charge claims reusing the underlying payment's UETR were flagged as duplicates and aborted. So a UETR generator bug that repeats values will pass a Fedwire-only test suite and then break the first time a payment leaves the country.

FedNow's message ID has its own structure, quoted in Jack Henry's gap analysis: an 8-digit date (CCYYMMDD), the sender's 9-character FedNow connection party ID, then a sender reference of up to 18 characters.

Pay.UK's New Payments Architecture documents mandate UETRs "across the payment lifecycle" for the system intended to replace Faster Payments, so the UK retail gap should close eventually. Today, an FPS payment has no UETR, and the UK rails comparison explains what you get instead.

What Happens to the UETR in a pacs.004 Return or camt.056 Recall?

A return does not get a UETR of its own. pacs.004 has no PmtId/UETR; it carries TxInf/OrgnlUETR pointing at the payment being returned. Deutsche Bank's high-value formatting guide marks OrgnlUETR as 1..1 and says it "must transport the UETR of the underlying payment". Fedwire also makes the original UETR mandatory on returns, while the original InstrId, EndToEndId and TxId are optional.

MessageWhere the original UETR livesNotes
pacs.004 returnTxInf/OrgnlUETRMandatory on CBPR+, Fedwire and T2
camt.056 recallUndrlyg/TxInf/OrgnlUETRCBPR+ also requires OrgnlEndToEndId
camt.029 recall answerCxlDtls/TxInfAndSts/OrgnlUETRMandatory on CBPR+
pacs.002 statusTxInfAndSts/OrgnlUETROptional in T2
pacs.028 status requestTxInf/OrgnlUETR0..1 in SEPA
pacs.009 COVPmtId/UETRSame value as the underlying pacs.008

Two edge cases will catch a naive "one UETR, one row" model.

A return of a return keeps pointing at the first payment. The Fed's FAQ spells it out: OrgnlMsgId becomes the incoming pacs.004's message ID, but OrgnlUETR stays the UETR of the original pacs.008 or pacs.009. One UETR can therefore own three or more money movements.

Split payments go the other way. In the Swiss SIC system, when a bank splits a payment, "a new, unique UETR must be generated for each split payment", the original UETR "may not be forwarded", the code SPLI is added, and the EndToEndId is carried over to link the parts. There, the EndToEndId is the join key and the UETR is not.

How Do You Track a Payment by UETR? gpi Status Codes

The UETR is the lookup key for Swift's gpi Tracker, and since the end of 2020 every bank has had to confirm the credit or rejection of each MT 103 to it. Most engineers never call the Tracker directly. You see it through a bank API, such as JPMorgan's track-and-trace endpoint, which returns an ISO status plus a gpi reason code:

Status / reasonMeaning
ACCCBeneficiary credited. Final.
ACSP / G000Passed to the next bank, which will keep updating the tracker
ACSP / G001Passed to a bank that cannot update the tracker. Tracking stops here.
ACSP / G002Credit may not be confirmed the same day
ACSP / G003Waiting for documents from the beneficiary
ACSP / G004Waiting for cover funds
RJCT + reasonRejected, with a code such as AC01, AC04, BE01, RR03 or DUPL. Final.
G001 is the one to model carefully. It is neither success nor failure, and no further update will arrive, so a poller that waits for ACCC will wait indefinitely. Give it a terminal "untracked" state and fall back to the beneficiary's confirmation. G003 and G004 explain most of the "where is my wire" tickets. Swift's October 2024 figures show 90% of cross-border payments reaching the destination bank within an hour, but only 43% reaching the end customer's account in that time. The delay is at the last bank, and the reason code usually tells you which kind.

Which Payment ID Should You Reconcile On in camt.053?

The honest answer is whichever your bank populates, and banks differ. camt.053 and camt.054 put references in NtryDtls/TxDtls/Refs: MsgId, PmtInfId, InstrId, EndToEndId, TxId, UETR in newer versions, and the bank's own AcctSvcrRef.

The GNU Taler project documented what it actually received over EBICS. German banks populated TxId everywhere and the UETR was "never found in practice". Swiss banks populated AcctSvcrRef and UETR and never TxId. On-us transfers carried only AcctSvcrRef. Their fix was three nullable columns with a check that at least one is present. That design is correct, and I would copy it:

create table bank_entry_ref (
  entry_id        bigint primary key,
  uetr            uuid,
  tx_id           varchar(35),
  acct_svcr_ref   varchar(35),
  end_to_end_id   varchar(35),
  check (num_nonnulls(uetr, tx_id, acct_svcr_ref) >= 1)
);

create index on bank_entry_ref (uetr) where uetr is not null; create index on bank_entry_ref (end_to_end_id) where end_to_end_id <> 'NOTPROVIDED';

Note what is absent: a unique constraint on uetr. A payment, its cover, its return and its fee adjustments can all share one.

If your bank still sends MT940, none of this fits. Field 61 gives you 16 characters per reference, which holds neither a 35-character EndToEndId nor a 36-character UETR. The MT940 vs camt.053 comparison has the field-by-field detail.

Should UETR Be Your Primary Key? My Position

No. Use it as the correlation key and keep your own primary key.

The UETR identifies a payment obligation across institutions. Your ledger records money movements, and one obligation produces several. A pacs.008, the pacs.009 COV that funds it and a later pacs.004 are three postings with one UETR. On SIC a single instruction becomes several UETRs. On SEPA, FPS and ACH there may be no UETR at all.

So the shape I'd build is: an internal payment ID as the primary key, a UETR column that is always populated (generate a v4 even on rails that ignore it), and per-rail reference columns for TxId, IMAD or trace number. Deduplicate outbound submissions with your own idempotency key, because Fedwire proves a rail will not always do it for you.

My prediction: TxId fades on high-value rails. A 2019 Eurosystem design note already described the UETR replacing the transaction ID in T2. Retail schemes will take longer. SEPA's 2025 rulebooks still treat UETR as an optional element with no usage rule, and nothing public suggests that changes before the next rulebook cycle.

What This Means for Payment Engineers

1. Unit-test your UUID generator against the schema regex. Lowercase, version 4, variant [89ab]. A switch to UUID v7 for index locality is a production incident on CBPR+. 2. Generate one UETR per payment obligation and reuse it for the cover. Generate a new one for a charge claim or any separate movement. 3. Never put a unique index on EndToEndId. Treat NOTPROVIDED as null. If you control the pain.001, always set a real value of 35 characters or fewer. 4. Keep InstrId to 16 characters if a payment could ever route over Swift or Lynx. 5. Match returns on OrgnlUETR first, then OrgnlEndToEndId plus amount and settlement date. T2 itself matches recalls on that combination. 6. Store every reference the statement gives you in its own column. Ask each bank for a sample camt.053 before you design the matcher, because the populated fields vary by country. 7. Model ACSP/G001 as a terminal untracked state in any gpi status poller.

The short version of the comparison: the EndToEndId belongs to the customer, the TxId to the first bank, the InstrId to each hop, and the UETR to the payment itself. Key on the last one where it exists, and keep the others for the rails where it does not.

I'm Tom Wang, and I write about the engineering underneath payment rails. Rail specifications change with each release, so check the current implementation guide before you hard-code any rule from this page.