Since 9 October 2025, every bank in the euro area has to check the payee's name before
a euro credit transfer leaves your account. It's called Verification of Payee (VoP),
and it comes from the EU Instant Payments Regulation. Bulgarian payment providers join
on 1 January 2027; banks in Czechia, Denmark, Hungary, Poland, Romania and Sweden on
9 July 2027.

The idea is simple. You type a name and an IBAN, and the payee's bank answers one of
four things: Match, Close Match (with the real name, so you can decide), No
Match
, or Verification not possible. A Close Match on a misspelled name saves a
payment; a No Match on a fraudster's IBAN saves your money.

The European Payments Council standardised the API between banks in detail. What it did
not standardise is the matching itself. Each responding bank chooses its own
algorithm and carries the liability for it. So I spent the last few weeks building an
open-source one, nameproof, and this post
is about what I learned.

nameproof playground: a Greek name matches, a typo is a close match, Siemens AG vs Siemens Energy AG is a close match, Ahmed vs Hamid is no match

The problem with a fuzzy score

The obvious approach is a fuzzy string similarity: normalise both names, compute a score
from 0 to 100, and pick thresholds for Match and Close Match. Here's what a typical
order-insensitive fuzzy score (token-sort ratio, with accents folded) gives on real cases:

Payer types Account holds Same entity? Fuzzy score
John Smith John Michael Smith yes 71
Siemens AG Siemens Energy AG no 74
Jon Smyth John Smith yes (typo) 84
Maria Garcia Mario Garcia no 92
Giorgos Papadopoulos Γιώργος Παπαδόπουλος yes 5
Volodymyr Zelenskyi Володимир Зеленський yes 5

Look at the first and last few rows. The same person scores lower than a different
person or a different company, so no single threshold works: set it high and you reject
real customers; set it low and Maria matches Mario. Fuzzy matching also scores
5/100 for names stored in another script, which across Greece, Cyprus, Bulgaria and
every bank with Ukrainian, Russian or Arabic-speaking customers is not an edge case.

A false No Match costs a customer and maybe a payment. A false Match is worse: it's
exactly the fraud VoP exists to stop. A score can't tell those apart; rules can.

What the EPC actually recommends

The EPC publishes non-binding Recommendations for the Matching Processes
(EPC288-23, version 2.0 from November 2026). In short:

  • Clean up first: ignore case, remove punctuation and titles, and convert the stored name into Latin characters before comparing.
  • Match only when the first and last name (or the company name) are identical after clean-up. Matching one holder of a joint account is enough.
  • Close Match for small deviations: a spelling mistake, two swapped letters, an initial instead of the first name, a sound-alike letter, and, for companies, abbreviations or words in a different order.
  • No Match for everything else.

That's a much better foundation than a threshold. "Identical after clean-up" is
explainable to a customer, an auditor and a regulator. The hard part is making the
clean-up good enough.

How nameproof does it

from nameproof import match match("Giorgos Papadopoulos", ["Γιώργος Παπαδόπουλος"]) # MATCH, explanation=('transliterated',) match("Jon Smyth", ["John Smith"]) # CLOSE_MATCH, suggested_name='John Smith', explanation=('fuzzy_token',) match("Maria Garcia", ["Mario Garcia"]) # CLOSE_MATCH: a spelling difference is never a Match 

Every name is expanded into the spellings worth comparing, each with a trust level:

  • Official romanisations can Match. Greek uses ELOT 743, the scheme in Greek passports. Cyrillic uses ICAO 9303, BGN/PCGN, the Bulgarian and Ukrainian national systems and Serbian Latin, chosen by the letters present. Новак Ђоковић becomes Novak Đoković and then Djokovic.
  • Informal romanisations only Close Match. Yannis for Γιάννης is a common spelling, but it isn't a standard, so the payer sees the real name.
  • Arabic never Matches. Arabic usually leaves short vowels unwritten, so nameproof compares consonant skeletons: Mohammed, Muhammad and محمد all reduce to mhmd. That's useful, but lossy: Mohamed and Mahmoud share a skeleton too. So Arabic gives at most a Close Match, and Ahmed vs حامد (Hamid) is still No Match.

On top of that come the details that make names hard: diacritics (Wałęsa), letter
expansions (Müller = Mueller), word order (Hungarian puts the surname first),
particles (van, de, al), hyphenated names, titles, generational suffixes, joint
accounts, patronymics, and about 1,400 company legal forms from the GLEIF ISO 20275
list. GmbH is recognised as the same legal form as Gesellschaft mit beschränkter
Haftung
(a Close Match under the EPC rules, since it's written differently), while
GmbH vs AG is flagged as a different kind of company.

Every verdict comes with stable reason codes (transliterated, fuzzy_token,
missing_name_part…) that never contain any part of a name, and every policy has a
version, so the same input always gives the same answer.

Measuring it in public

I built nameproof-bench: 6,000 labelled name pairs from Wikidata across Greek,
Cyrillic, Arabic, seven Latin-script languages and companies. For privacy it uses only
people who have died. Variations include typos, initials, reordered and ASCII-typed
names; negatives include different people who share a surname.

nameproof token-sort fuzzy Levenshtein exact
Same person wrongly rejected 9.5% 63.8% 76.5% 89.8%
Same person matched automatically 41.7% 26.2% 11.3% 10.2%
Different people falsely matched 0.0% 0.0% 0.0% 0.0%

Everything nameproof neither matches nor rejects is a Close Match, which is by design
for typos and initials. I kept the hard cases in rather than filtering them out:
exonyms (John Paul II / Jan Paweł II) and company names that were translated rather
than transliterated. Removing them would have flattered my own numbers.

The review that humbled me

Before making the repository public I asked for an adversarial review: try to make
nameproof say Match for two different parties, or reveal an account holder's name. It
found 17 real problems, and six of them were false Matches:

  • Siemens AG vs Siemens Energy AG was a Match: the extra word passed as a "middle name".
  • Hassan Ibn Ali vs Hassan Abu Ali (son of vs father of) was a Match, because particles were dropped on both sides.
  • Smith vs Smith AG was a Match, because removing the legal form left one word on each side.
  • Sending just the word "GmbH" returned the account holder's full company name as a Close Match. Anyone with an IBAN could use that to look up names.
  • One crafted request took 20 seconds of CPU.

My benchmark showed 0.0% false matches throughout, because its negatives weren't
adversarial. The lesson I took: every safety check has to look at the name as it was
before you removed anything
. Titles, legal forms, particles and the other holders of a
joint account all make a name shorter, and a shorter name looks more like everything.
nameproof now counts words before stripping, needs a real name word (not a particle,
title or legal form) to justify revealing a name, and caps the work per request. All 17
cases are permanent regression tests.

Try it

  • In your browser, where nothing you type leaves the page: nadaboulaares.github.io/nameproof
  • As a library: pip install nameproof
  • As an EPC VoP responder (EPC103-24 v1.1.1, tested against the EPC reference test cases): docker pull ghcr.io/nadaboulaares/nameproof

It's Apache-2.0 and pure Python, and it runs entirely on your own infrastructure.

If you work on VoP, Confirmation of Payee or KYC name screening, I'd love to hear where
you'd draw the line between Match and Close Match, and which names break your current
matcher. I'm also looking for two or three banks or payment institutions to pilot it
before their deadline: details here.

nameproof is a matching tool, not legal or compliance advice.