# The BACS hash: HMRC's payment check that sounded good and caught nobody

> For eleven years payroll software had to send HMRC a SHA-256 hash of every Bacs payment so they could check the money matched. It couldn't tell who was paid, and one email retired it mid-year.

- Author: Sam Brown (Payroll Explained)
- Published: 2026-08-23
- Updated: 2026-09-11
- Tags: RTI, FPS, BACS, SHA-256, HMRC, payroll software, fraud
- Canonical: https://payrollexplained.uk/blog/the-bacs-hash/

---

When RTI came in, HMRC added a field to the Full Payment
Submission called **data item 118**. It was a 64 character SHA-256 hash. If
you paid staff by Bacs your payroll software had to work it out for every
employee, every payday, and send it to HMRC.

The idea was that HMRC could cross-check the hash from your payroll software
against a hash Bacs worked out from the actual payment. If they matched, the
wages you *said* you paid were the wages you *actually* paid.

It sounds good. In reality it couldn't tell who was being paid, it produced
the same hash for different payments all the time, and in eleven years nobody
has ever pointed to a single thing it caught. Then in February 2023, halfway
through the tax year, HMRC retired it with one email. It still had to be
computed for the rest of the year, because they'd decided too late to take it
out of the schema!

<div class="facts">
<div><strong>11</strong><span>tax years it was mandatory</span></div>
<div><strong>59,319</strong><span>possible values of the "random" part</span></div>
<div><strong>2</strong><span>things HMRC got back: the hash and the amount</span></div>
<div><strong>0</strong><span>published cases of it catching anything</span></div>
</div>

## Where it came from

In 2009 David Gauke (shadow Treasury minister, then the actual minister) sat
down with payroll software people with a big idea: what if the banks worked
out your tax? You'd send your gross payroll to a "centralised deductions"
system, it would take off PAYE, NI and student loan on the way through, keep
the tax and forward the net pay. Payroll software basically stops existing in
that world.

HMRC consulted on this in July 2010, alongside a tamer idea called Real Time
Information. The industry's response to centralised deductions was basically
"absolutely not", and it got dropped. RTI survived, mainly because Universal
Credit needed a real time feed of what people earned.

But once you drop the banks doing the deductions, you lose the one thing that
gave you for free: knowing the money reported is the money that actually
moved. RTI on its own is just an employer telling HMRC "trust me, I paid Dave
£1,800". So someone asked how do we know the FPS matches the Bacs payment, and
the answer was a hash.

<figure>
<div style="overflow-x:auto">
<svg viewBox="0 0 720 250" role="img" aria-label="Timeline from the 2009 centralised-deductions idea through the hash going live in September 2012 to its withdrawal in February 2023" style="width:100%;min-width:600px;height:auto;font-family:inherit">
<line x1="45" y1="120" x2="695" y2="120" stroke="var(--border)" stroke-width="2"/>
<circle cx="70" cy="120" r="6" fill="var(--text-muted)"/>
<text x="70" y="80" font-size="12" font-weight="600" fill="var(--text)" text-anchor="middle">2009</text>
<text x="70" y="96" font-size="11" fill="var(--text-muted)" text-anchor="middle">"banks calc the tax"</text>
<circle cx="215" cy="120" r="6" fill="var(--text-muted)"/>
<text x="215" y="150" font-size="12" font-weight="600" fill="var(--text)" text-anchor="middle">Jul 2010</text>
<text x="215" y="166" font-size="11" fill="var(--text-muted)" text-anchor="middle">PAYE consultation</text>
<circle cx="360" cy="120" r="6" fill="var(--text-muted)"/>
<text x="360" y="80" font-size="12" font-weight="600" fill="var(--text)" text-anchor="middle">2011</text>
<text x="360" y="96" font-size="11" fill="var(--text-muted)" text-anchor="middle">central deductions dropped;</text>
<text x="360" y="110" font-size="11" fill="var(--text-muted)" text-anchor="middle">hash compromise born</text>
<circle cx="505" cy="120" r="6" fill="var(--accent)"/>
<text x="505" y="150" font-size="12" font-weight="600" fill="var(--text)" text-anchor="middle">1 Sep 2012</text>
<text x="505" y="166" font-size="11" fill="var(--text-muted)" text-anchor="middle">hash live (reg 67CA)</text>
<circle cx="650" cy="120" r="6" fill="var(--accent)"/>
<text x="650" y="80" font-size="12" font-weight="600" fill="var(--text)" text-anchor="middle">13 Feb 2023</text>
<text x="650" y="96" font-size="11" fill="var(--text-muted)" text-anchor="middle">switched off by email</text>
<text x="577" y="212" font-size="11" fill="var(--text-muted)" text-anchor="middle" font-style="italic">eleven tax years of mandatory hashing (2012/13–2022/23)</text>
</svg>
</div>
<figcaption>From the 2009 idea to the 2023 withdrawal.</figcaption>
</figure>

## What it actually was

If you paid staff by Bacs Direct Credit under your own Service User Number
(SUN, the ID that says "this business submits to Bacs directly") you had two
things to do:

1. In **field 7 of the Standard 18 Bacs payment file**, put a small random tag.
2. On the **FPS for that employee**, put a SHA-256 hash made from that tag plus
   the payment details.

VocaLink (who run Bacs) would work out the same hash from the payment as it
went through, send it to HMRC, and HMRC would check the two matched.

The hash input is an ASCII string made of four parts stuck together: the
4 character sub-reference from field 7, the originator's sort code, the
recipient's sort code, and the amount in pence padded to 11 digits. Have a go.
It starts on HMRC's own worked example from the guidance, so the first thing
you see is the published hash checking out.

<bacs-hash-demo>Interactive calculator: build the 27-character string and SHA-256 it yourself (web version only). HMRC's example input /A..10000030914400000125671 hashes to a8e88f215cc98f40a2d0c47c49d0b09f4593d9bb81aef118202987a8cc0e3689.</bacs-hash-demo>

If you'd rather do it in a terminal:

```bash
$ printf '/A..10000030914400000125671' | sha256sum
a8e88f215cc98f40a2d0c47c49d0b09f4593d9bb81aef118202987a8cc0e3689
```

That matches the published hash exactly.

Two things about that input matter a lot.

**The "4 character sub-reference" is really 3 characters.** The first one is
always a forward slash, which has to sit in character position 32 of the Bacs
file. The three after it are the actual random bit, and each comes from an
alphabet of hyphen, full stop, forward slash, 0-9 and A-Z. That's 39 possible
characters in each of 3 slots, which matters for the collision maths below.

**The account number isn't in there.** Try changing it in the calculator
above. Nothing happens, because it was never part of the hash. HMRC
confirmed this in an FOI response.

## What HMRC actually received

In 2013 Adrian Kennard (a software developer and ISP owner who blogs as RevK,
and ran his own payroll through his own software) put in a Freedom of
Information request asking HMRC to say exactly what Bacs sent them. The
answer:

> VocaLink regenerates the hash cross reference, when they receive the payment
> instruction and send the (1) hash cross reference and (2) the related payment
> amount to HMRC... VocaLink do not provide any other information about
> individual payments to HMRC, and HMRC does not have access to the Bacs source
> information or bank account details.

So HMRC gets the hash and the amount. That's it. They never see who was paid.
No account number, no name, nothing that links the payment to a person.

If the employer picks the random tag, the employer picks the amount, and the
payment can go to any account, what's being checked? RevK put it bluntly on
his blog at the time:

> The payment can be to anyone (even yourself, which is perfectly valid as a
> BACS payment). [...] It checks no more than the submitter has access to
> BACS. [...] Real fraudsters have no problem as it is simple for them to pass
> the tests for the hash.

To get a matching hash all you have to do is make *a* Bacs payment of the
right amount from the right sort code with the right tag. Pay it to yourself.
Pay it into the company's own account. The hash matches, the FPS
"reconciles", and not a penny needs to have reached the employee you
reported.

When RevK put this to HMRC in a follow up FOI, asking them to just admit the
hash "does not prove a payment to the individual concerned, and so is totally
pointless", they basically refused to talk about it. They also refused to say
what the whole thing had cost.

## The collision problem

Everything above is sourced. This bit is mine.

HMRC's guidance leans on the word "unique". It says the combination of
sub-reference, sort codes and amount gives "a sufficient degree of uniqueness
to allow effective matching", and that when someone gets paid the same amount
regularly, the random sub-reference is what tells the payments apart. So the
sub-reference is doing all the work. How much can it actually tell apart?

<div class="note mine">
<span class="label">My working</span>

39 characters in 3 positions gives 39 × 39 × 39 = **59,319** distinct
sub-references. That's log₂(59,319) ≈ 15.9 bits, so less than a 16-bit
number. The "random token" securing payroll reconciliation for the whole
country had less entropy than a `short`.

</div>

Now apply the birthday problem, the same maths that says 23 people in a room
gives you a 50/50 chance of two sharing a birthday. In any pool of payments
that share a sort code pair and an amount, how many do you need before two
land on the same sub-reference and produce hashes HMRC can't tell apart?

<bacs-collision-demo>Interactive simulator: birthday-problem collision odds for 59,319 sub-references (web version only). It's a coin flip at 287 identical payments, over 95% by 600, basically certain by 1,000.</bacs-collision-demo>

At 287 otherwise identical payments it's a coin flip. By 600 it's over 95%.
By 1,000 it's basically certain. (The exact product crosses 50% at k = 287,
which matches the standard `√(2N ln 2)` estimate of 286.8.)

Is a pool of hundreds of identical payments realistic? For one small
employer, no. But HMRC wasn't matching within one employer, it was matching
everything coming through Bacs nationally, and Bacs handles roughly 27
million payments a day (6.86 billion in 2025 over about 252 working days).
Round number salaries going through common high street sort codes aren't
rare, they're the norm. RevK noted in 2013 that HMRC had already admitted
they'd get duplicates, having left the one genuinely unique field, the Bacs
Service User Number, out of the hash completely.

So there are three separate problems with it:

1. The thing meant to tell payments apart can't reliably tell them apart.
2. The hash only proves the sender can use Bacs.
3. HMRC never sees who was paid.

## What it cost other people

<div class="facts">
<div><strong>42%</strong><span>of Bacs-paying employers surveyed paid extra to support field 7</span></div>
<div><strong>£30,000</strong><span>top of the range spent on upgrades in that survey</span></div>
<div><strong>0 of 10,000</strong><span>12Pay clients paying staff via their own Bacs SUN</span></div>
</div>

Before RTI, field 7 of the Standard 18 format was basically unused. Suddenly
every employer submitting under their own SUN had to write a random value into
it on every payment, and their bank's Bacs software had to let them. That
cost money. A 2013 survey by the CIPP and Payroll Alliance (156 respondents,
71% of them paying staff by Bacs) found 42% of the Bacs payers had been
charged extra to get field 7 into their file. Of those, 39% paid between
£1,000 and £4,999 and 8% paid between £7,000 and £30,000.

Some banks' software wouldn't do it at all. RevK's bank, Lloyds, looked at
upgrading its Lloydslink Bacs bureau to support field 7, decided (in his
words) "it would cost too much", and shut the bureau down instead, with about
two months notice, at the end of February 2013. He moved to a Bacstel-IP
setup through another bureau at a bit over £2,000 a year, and later reckoned
"hundreds of Lloydslink customers had to change DD collection" because of it.
That's his account and his estimate. I haven't found Lloyds' side of it, and
the closure could have had other reasons too.

And hardly anyone it was aimed at was even paying by Bacs in the relevant
way. When the proposal came out in 2011, Tom McClelland of the payroll vendor
12Pay said that of his roughly 10,000 client companies, not one paid
employees through their own Bacs SUN. Most UK employers pay through internet
banking or Faster Payments, and neither of those has a hash at all. Yes, over
90% of the UK *workforce* is paid by Bacs Direct Credit, but that's because
they work for big employers. The long tail of small employers, the ones RTI
most wanted to keep an eye on, mostly never went near it.

HMRC's response to employers who didn't use Bacs was to warn developers that
filing without a hash would be treated as a "compliance investigation issue".

## How it ended

By 2017, when HMRC published its forty page RTI Post-Implementation Review,
the hash gets mentioned exactly once. Not under fraud detected or revenue
protected. It's in a bullet list of stakeholder complaints, four words long:

> BACS hash matching is problematic

Then on 13 February 2023 HMRC's Software Developers Support Team sent round
an email (Simon Parsons at PAYadvice kept a copy):

> HMRC has recently decided to withdraw the requirement for employers to
> supply an entry in the Bacs hash code field (data item 118) for 2023-24
> onwards. Unfortunately this decision was made too late to remove it from the
> 2023-24 RTI FPS schema; any supplied values will not be processed.

Too late to take the field out, so it stayed in the schema for another year.
Software kept computing it and HMRC ignored it.

<figure>
<div style="overflow-x:auto">
<svg viewBox="0 0 720 230" role="img" aria-label="Two timelines. The FPS field: live September 2012, withdrawn by email February 2023, ignored for 2023/24, removed from the schema 2024/25. The law, regulation 67CA: in force September 2012 and still in force in 2026." style="width:100%;min-width:600px;height:auto;font-family:inherit">
<text x="20" y="46" font-size="12" font-weight="600" fill="var(--text)">The field</text>
<text x="20" y="62" font-size="11" fill="var(--text-muted)">FPS item 118</text>
<line x1="130" y1="56" x2="530" y2="56" stroke="var(--accent)" stroke-width="3"/>
<line x1="530" y1="56" x2="600" y2="56" stroke="var(--accent)" stroke-width="3" stroke-dasharray="4 4"/>
<circle cx="130" cy="56" r="6" fill="var(--accent)"/>
<circle cx="530" cy="56" r="6" fill="var(--accent)"/>
<circle cx="600" cy="56" r="6" fill="var(--bg)" stroke="var(--accent)" stroke-width="2"/>
<text x="130" y="30" font-size="11" fill="var(--text)" text-anchor="middle">Sep 2012: mandatory</text>
<text x="530" y="30" font-size="11" fill="var(--text)" text-anchor="middle">Feb 2023: withdrawn by email</text>
<text x="600" y="86" font-size="11" fill="var(--text-muted)" text-anchor="middle">2023/24: sent, ignored</text>
<text x="600" y="100" font-size="11" fill="var(--text-muted)" text-anchor="middle">2024/25: gone from schema</text>
<text x="20" y="156" font-size="12" font-weight="600" fill="var(--text)">The law</text>
<text x="20" y="172" font-size="11" fill="var(--text-muted)">regulation 67CA</text>
<line x1="130" y1="166" x2="690" y2="166" stroke="var(--warn)" stroke-width="3"/>
<circle cx="130" cy="166" r="6" fill="var(--warn)"/>
<circle cx="690" cy="166" r="6" fill="var(--warn)"/>
<text x="130" y="140" font-size="11" fill="var(--text)" text-anchor="middle">Sep 2012: in force</text>
<text x="690" y="140" font-size="11" fill="var(--text)" text-anchor="end">Sep 2026: still in force</text>
<text x="690" y="196" font-size="11" fill="var(--text-muted)" text-anchor="end">never amended, never revoked</text>
</svg>
</div>
<figcaption>The field was withdrawn in 2023. The regulation that requires it is still on the books.</figcaption>
</figure>

There's also a loose end that still isn't tied off. Regulation 67CA of the
Income Tax (PAYE) Regulations 2003, the legal basis, is still in force as I
write this. legislation.gov.uk shows "no known outstanding effects", it's
never been revoked. Meanwhile data item 118 has been removed from the current
Data Items Guide completely. I searched the 2025/26 version and the words
"hash", "Bacs" and the item number just aren't there. So there's a legal duty
with no field to put it in.

## Things I worked out myself

The collision maths is one. Three smaller things from following the story to
the end:

**Nobody can point to it ever working.** I went looking for a single example
of the hash doing its job: a tribunal decision citing a mismatch, an NAO
value for money line, a compliance case, anything in the Post-Implementation
Review. There's nothing. That's not proof it never flagged anything, but for
something mandatory, in legislation, that cost banks and employers real money,
I'd expect at least one example.

**Faster Payments made it irrelevant anyway.** The hash only existed because
Bacs has a file with a field 7 to hide a token in. Faster Payments, which is
how a growing share of smaller employers pay staff, has no such field and
never had a hash. As more employers moved to Faster Payments, fewer and fewer
payments were covered by it anyway.

**ISO 20022 probably killed it.** The withdrawal email
gave no reason. But the timing lines up with Bacs moving towards ISO 20022
messaging under the New Payments Architecture, where the flat, fixed position
Standard 18 record ("solidus in character position 32" and all) doesn't have
a home. You can't mandate "field 7, positions 33 to 35" of a file format
you're retiring. I can't prove this was the trigger, it's an inference, but
"the file it lived in is being replaced" explains a silent withdrawal a lot
better than HMRC changing its mind about fraud. They never conceded the fraud
point even when RevK put it to them directly in 2013.

## What it was really for

Here's what the check could and couldn't see. Pick what the employer does
and compare the two hashes HMRC would have received.

<bacs-fraud-demo>Interactive scenario picker: which kinds of dodgy payment the hash catches and which it doesn't (web version only). Paying yourself at the same sort code for the same amount produces an identical hash.</bacs-fraud-demo>

If it didn't catch fraud, what was it for? I think it was there to make people
feel better. It let HMRC tell Universal Credit's designers, and themselves,
that RTI figures were "verified against actual bank payments", which sounds
great in a business case.

The lesson for developers is the usual one: a hash is only as good as the
least trusted input to it, and every input here was picked by the person
being checked. SHA-256 worked fine. It hashed an amount the employer chose,
a sort code the employer controlled and a random string the employer made up,
and HMRC couldn't see the account number, which was the one thing that
mattered.

## Sources

**Primary (HMRC / legislation):**

- [HMRC guidance: *Generating the cross reference (RTI hash)*](https://assets.publishing.service.gov.uk/media/6447eceb814c6600128d061f/RTI_Hash_Cross_Reference_Guidance.pdf) - now marked "Withdrawn"; contains the algorithm, the alphabet, and the worked example whose SHA-256 digest is verified above.
- [Regulation 67CA, Income Tax (PAYE) Regulations 2003](https://www.legislation.gov.uk/uksi/2003/2682/regulation/67CA) - the legal basis, still in force as at September 2026.
- [The Income Tax (PAYE) (Amendment No. 2) Regulations 2012 (SI 2012/1895)](https://www.legislation.gov.uk/uksi/2012/1895/contents/made) - inserted 67CA, effective 1 September 2012.
- [*Improving the operation of PAYE* / Real Time Information (policy background)](https://www.gov.uk/government/publications/real-time-information-improving-the-operation-of-pay-as-you-earn/real-time-information-improving-the-operation-of-pay-as-you-earn) - the 2010 consultation and the centralised-deductions proposal that RTI grew out of.
- [RTI Programme Post-Implementation Review (2017)](https://assets.publishing.service.gov.uk/media/5a821899e5274a2e8ab5778f/Real_Time_Information_programme_-_post_implementation_review_report.pdf) - the sole official reflection: "BACS hash matching is problematic."

**FOI (Adrian Kennard / WhatDoTheyKnow):**

- [*What information do BACS send to HMRC?*](https://www.whatdotheyknow.com/request/what_information_do_bacs_send_to) - HMRC confirms it receives only the hash and the amount, and never sees bank details.
- [*How much has the BACS hash cost?*](https://www.whatdotheyknow.com/request/how_much_has_the_bacs_hash_cost) - HMRC declines to give costs; contains RevK's on-record argument that the hash proves nothing.

**Contemporary commentary:**

- [RevK: *PAYE RTI BACS madness* (Jan 2013)](https://www.revk.uk/2013/01/paye-rti-bacs-madness.html), [*BACS Submissions* (Mar 2013)](https://www.revk.uk/2013/03/bacs-submissions.html) and [*BACS/RTI hash* (May 2013)](https://www.revk.uk/2013/05/bacsrti-hash.html) - the Lloydslink bureau closure and his move to another bureau, the "pay yourself" hole, and the acknowledged duplicates. The "hundreds of customers" figure is his, from a comment on the May post.
- [AccountingWEB: *Employers hit by BACS costs for RTI* (2013)](https://www.accountingweb.co.uk/tech/tech-pulse/employers-hit-by-bacs-costs-for-rti) - the CIPP / Payroll Alliance RTI Software Costs Survey: 42% of Bacs payers charged extra for field 7, upgrades of £1,000 to £30,000.
- [AccountingWEB: *HMRC hashes up RTI compromise* (Aug 2011)](https://www.accountingweb.co.uk/tax/hmrc-policy/hmrc-hashes-up-rti-compromise) - the "kludge" reaction and 12Pay's "not one of 10,000 clients pays by Bacs SUN".
- [PAYadvice (Simon Parsons): *HMRC drops the 'best practice' BACS Hash* (Feb 2023)](https://payadvice.uk/2023/02/14/hmrc-drops-the-best-practice-bacs-hash/) - the withdrawal email in full, and the David Gauke origin story.
- [Pay.UK Bacs statistics](https://www.wearepay.uk/what-we-do/payment-systems/bacs-payment-system/) and [Bacs Direct Credit introduction](https://www.bacs.co.uk/media/cmuccdew/bdc-introduction.pdf) - annual volumes and the "over 90% of the UK workforce" figure.

*This describes a payroll obligation that ran from September 2012 to April
2023, plus my own analysis where flagged. Not tax advice. The collision
arithmetic is reproducible from the figures given; the ISO 20022 explanation
is my inference, not an HMRC statement.*
