
Verification of Payee: what a close match costs you
Roughly one in four Verification of Payee checks in the euro area comes back as a close match rather than a match. Not a pass, not a fail. A four character code and a slightly different name, handed to your payment screen with a decision attached. Whatever your product does next is the real engineering work here, because the API part is genuinely small: one POST, a five second budget, an enum with four values.
The integration is a week. Close match handling is what you will still be arguing about a year later, and it is where the liability actually sits.
What lands on 20 September 2026
The European Payments Council issued version 1.1 of the Verification Of Payee scheme rulebook (EPC218-23) on 16 March 2026. It takes effect on 20 September 2026 at 03:30:00.000 CET, alongside the matching Inter-PSP API specifications (EPC103-24 v1.1.1) and the updated API Security Framework. The rulebook's own change log describes v1.1 as "urgent updates identified during the launch of the scheme", which tells you most of what you need to know about how the first year went.
The obligation itself is older. Under the EU Instant Payments Regulation, euro-area PSPs have had to offer VoP since 9 October 2025, and PSPs in non-euro EEA states have until 9 July 2027. It applies to SEPA credit transfers generally, not only instant ones, so this is not a niche path in your code. It is the default path.
Four outcomes, and only one of them is interesting
A VoP request is a POST to a payee verification endpoint carrying the name, the IBAN, and the BICs of both agents:
POST /api/vop/v1/payee-verifications
{
"party": { "name": "Dupont Jean" },
"partyAccount": { "iban": "BE12345678901234" },
"partyAgent": { "financialInstitutionId": { "bicfi": "ABCDBEBBXXX" } },
"requestingAgent": { "financialInstitutionId": { "bicfi": "ABCDBEB0XXX" } }
}
The response is one field:
| Code | Meaning | Extra payload |
|---|---|---|
MTCH | Match | none |
CMTC | Close match | matchedName, the name the bank holds |
NMTC | No match | none |
NOAP | Verification check not possible | none |
Three things about that table bite teams on first integration.
All four are HTTP 200. NOAP is a successful HTTP call that verified nothing. A client that branches on status code and treats any 200 as a pass has shipped a silent failure, and it will not show up in your error rate.
CMTC is the only outcome that returns data. The responding PSP sends back matchedName, the account holder name as it actually appears in its records, with a standing instruction to apply data minimisation and never disclose other holders on a joint account. That single string is the difference between a useful screen and a dead end, and it is the only thing your user can act on.
And the ID plus IBAN variant (VAT number or LEI instead of a name) returns partyIdMatch and structurally cannot return a close match. Identification codes either match exactly or they do not. If your corporate customers can supply an LEI, that path removes the entire ambiguity class discussed below.
The rulebook sets a maximum execution time of 5 seconds for the requesting PSP to get a response, and states a preference for 1 second or less. Observed ecosystem latency since go-live has averaged around 692 milliseconds, so the 5 second ceiling is a timeout budget, not a target. If nothing arrives inside it, you must tell the user the check could not be performed and discard any response that turns up late. That last clause matters: a late response is a stale answer about a payment the user may already have authorised, and the scheme tells you to drop it rather than reconcile it.
One in four, and you do not control which
Since the euro-area go-live, the ecosystem-wide distribution reported by Worldline has been 61% match, 25% close match, 14% no match. Their own tuned matching module produced 52% match, 38% close match, 10% no match on the same kind of traffic.
VoP outcome distribution since euro-area go-live
Read those two rows side by side and the design problem appears. Better matching did not raise the match rate. It converted no matches into close matches, moving 4 points out of hard rejection and into the ambiguous middle. That is the correct outcome for the scheme, whose stated aim is to avoid unnecessary no match responses and payment initiation friction. It is also a straight transfer of work from the bank's rejection logic into your interface.
The reason the numbers move at all is that the matching algorithm is not standardised. The EPC's recommendations for the matching processes (EPC288-23) are explicitly guidance, and the document says so plainly: the responding PSP "remains entirely free to apply different criteria" and "takes up the liability for providing such response". The recommended clean-up before matching (ignore case, fold diacritics, strip punctuation and honorifics, trim whitespace) is common sense. The close match scenarios are where the discretion lives:
- a spelling mistake within a threshold the PSP itself sets, for example a Levenshtein distance
- two transposed letters in a first or last name
- an initial plus the surname instead of the full first name
- a letter or two swapped for phonetic equivalents
- for legal persons only: words in the wrong order, or a commonly accepted abbreviation
Each responding PSP picks its own threshold, and may apply stricter rules to legal persons on the reasoning that a slightly altered company name is a more likely fraud attempt. So the same name against the same account number can return MTCH at one bank and CMTC at another. The outcome is not a stable property of the payee, it is a property of the counterparty's bank on the day you asked. Cache accordingly, which is to say mostly do not.
The scheme standardises the wire, not the screen
Here is the sentence in the rulebook that most integration plans skip: "the way the Requester is informed and the content of the notification messages are left to the discretion of the Requesting PSP."
The wire format is fully specified. The screen is not specified at all. And the screen is where the money moves.
That is the whole design constraint, and it resolves into three concrete rules.
A close match screen needs two distinct actions, not one. "Use the name the bank holds" and "pay anyway" are different user intents with different evidence trails, and an OK button collapses them into an unprovable one. Log which one was pressed.
The matchedName has to be shown. A close match without the bank's version of the name gives the payer nothing to reason about, and they will click through by reflex. With it, the difference between "Dupont Jean" and "Dupond Jean" is a decision a human can actually make in two seconds.
And NOAP needs its own copy. The scheme requires that on no match, on verification not possible, and on no response at all, you tell the payer that authorising may send funds to an account not held by the intended payee. Those are the same words for three very different underlying conditions, so keep them distinct in your metrics even when they share a screen. A sustained NOAP rate against one BIC is an outage at that bank, and it should page someone rather than silently degrade every payment to that institution.
Not a SEPA quirk
The pattern is showing up on every real-time rail, because authorised push payment fraud (the payer is tricked into sending to the wrong account) is not specific to euro payments. The UK has run Confirmation of Payee since 2020 with the same three-way outcome and its own reason codes, including one for "you said business, the account is personal, and the name is a close match". In the US, the Federal Reserve's Payee Name Verification sits in the FedDetect services portfolio, and the FedNow network intelligence API went live for early adopters on 28 April 2026, letting a sender pre-check receiver account signals before releasing an instant payment.
| Live since | 9 Oct 2025, euro area | 2020 | Payee Name Verification, plus FedNow network intelligence API from 28 Apr 2026 |
| Close match returned | Yes, CMTC plus matchedName | Yes, close match plus name and reason code | Name to account verification, no scheme-wide close match enum |
| Response budget | 5 seconds, 1 second preferred | Real time at initiation | Pre-check before release |
| Who owns the algorithm | Responding PSP, full discretion and liability | Responding bank | Fed service plus the institution's own rules |
| Mandatory | Yes, under the Instant Payments Regulation | Yes for the largest banks, phased by the regulator | No, opt-in service |
| Bulk support | No bulk in scheme, PSPs debatch | Per-payment | Per-payment |
If you build for more than one geography, normalise all of them into one internal outcome type early, something like verified | ambiguous | rejected | unknown, keeping the rail's raw code alongside it. Letting rail-specific enums leak into your checkout state machine ages as badly as letting an acquirer's decline codes leak into your order model. The authorisation and settlement split that makes real-time payment rails work the way they do is exactly why the pre-check has to be its own step with its own persisted result.
What to build before September
Most of this is not VoP-specific. It is the boring plumbing that makes an ambiguous third-party answer survive contact with your ledger.
Persist the outcome code, not a boolean. Store partyNameMatch, the matchedName if present, the responding BIC, the timestamp, and which button the user pressed. When a customer disputes a misdirected payment eighteen months later, that record is your entire defence. A verified: true column is worth nothing.
Re-verify on any change to name or IBAN. A cached pass against an edited payee is a bug with a payout attached. Verification results are cheap to get and expensive to be wrong about, and the retry semantics belong in the same place as the rest of your payment idempotency handling so a user hammering a slow screen does not create two intents with two different verification states.
Design the bulk queue on purpose. The scheme does not support bulk requests, so PSPs debatch and verify line by line. A 200 line supplier run at a 25% close match rate produces roughly 50 rows needing a human, and if your operations screen does not group and bulk-resolve them, you have built an unshippable payroll process. One real case reported after go-live had eight million euro held because a trading name did not match the registered legal entity name.
Fix your own name data first. The single highest-leverage change is collecting the payee's exact registered name at onboarding rather than a display name, and telling your users to give suppliers their official name. Every abbreviation, accent and "Ltd" that you tolerate in your database becomes a close match downstream.
Feed the outcome codes into reconciliation. Verification results are payment metadata and belong in the same store you reconcile from, so "why did this payment stall" is one query rather than a support ticket. The payment reconciliation engine patterns apply unchanged: the outcome is another attribute that has to survive from initiation to settlement without being flattened.
The blunt version: VoP does not tell you whether a payment is safe. It tells you how confident someone else's bank is about a string, on a scale it defined and can change without telling you. Treating that as a boolean is the bug. Treating it as evidence you present, log and can produce later is the product.
If you are wiring VoP, Confirmation of Payee or a FedNow pre-check into an existing checkout and want a second pair of eyes on the state machine before September, our team does this kind of payment rails work every week, and you can get in touch.
New posts, in your inbox
Get an email when we publish a new deep-dive. No spam, unsubscribe anytime.