---
title: "Put a checked structured reference on · Agent Wranglers"
url: https://agentwranglers.se/playbook/payments/bank-transfers-and-local-methods/structured-payment-reference
id: PAY-73
level: Recommended
applies: payments
updated: 2026-10-27
build: a2a6bdfed830
tokens: 1334
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Put a checked structured reference on every bank-transfer invoice

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [Bank transfers and local methods](https://agentwranglers.se/playbook/payments/bank-transfers-and-local-methods.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers.md). Updated 2026-10-27.

- ID: PAY-73
- Level: Recommended
- Chapter: [Bank transfers and local methods](https://agentwranglers.se/playbook/payments/bank-transfers-and-local-methods.md)
- Rule: Give every invoice paid by bank transfer a unique structured reference with a check digit, in the format the payer's banks check. Match incoming payments by that reference alone.
- Check: A unit test builds references for known invoice numbers in each format you use and compares them with published examples. A test payment file with a reference that fails the check digit lands on the unmatched list.
- Applies when: The project takes payments.
- How: A structured reference lets the bank refuse a mistyped reference and lets your system match each payment without a person. The international form is the RF creditor reference of ISO 11649, which works in every SEPA country. It is "RF", two check digits computed with modulo 97, and up to 21 characters of your own. Several countries also run a national format that their banks check, listed in the table below. Build the reference from the invoice number, and add the check digit. Turn on the reference check for your account where the bank offers one, as Bankgirot does in Sweden. Payments that arrive without a valid reference go to the unmatched list from [Reconcile every payout](https://agentwranglers.se/playbook/payments/payment-events-and-reconciliation/payout-reconciliation.md).

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

| Country | Reference | Check digit |
| --- | --- | --- |
| Any SEPA country | RF creditor reference, ISO 11649 | Modulo 97, two digits after "RF" |
| Sweden | OCR number on bank giro and plus giro | Modulo 10, the Luhn method |
| Norway | KID number, 2 to 25 digits | Modulo 10 or modulo 11, set per agreement |
| Finland | Reference number, viitenumero | Weights 7, 3 and 1 from the right |
| Denmark | FIK payment ID on the +71, +73 and +75 forms | Modulo 10 |

### Python example

```python
def ocr_reference(invoice_number: int) -> str:
    digits = str(invoice_number)
    total = 0
    for position, char in enumerate(reversed(digits)):
        value = int(char) * (2 if position % 2 == 0 else 1)
        total += value - 9 if value > 9 else value
    return digits + str((10 - total % 10) % 10)
```

### JavaScript example

```js
export function ocrReference(invoiceNumber) {
  const digits = String(invoiceNumber)
  let total = 0
  for (let position = 0; position < digits.length; position++) {
    const value = Number(digits[digits.length - 1 - position]) * (position % 2 === 0 ? 2 : 1)
    total += value > 9 ? value - 9 : value
  }
  return digits + String((10 - (total % 10)) % 10)
}
```

### JavaScript example 2

```js
export function ocrReference(invoiceNumber) {
  const digits = String(invoiceNumber)
  let total = 0
  for (let position = 0; position < digits.length; position++) {
    const value = Number(digits[digits.length - 1 - position]) * (position % 2 === 0 ? 2 : 1)
    total += value > 9 ? value - 9 : value
  }
  return digits + String((10 - (total % 10)) % 10)
}

export function rfReference(creditorReference) {
  const rearranged = `${creditorReference}RF00`.replace(/[A-Z]/g, letter => String(letter.charCodeAt(0) - 55))
  const checkDigits = 98n - BigInt(rearranged) % 97n
  return `RF${String(checkDigits).padStart(2, '0')}${creditorReference}`
}
```

## Sources

- [ISO 11649 structured creditor reference](https://www.iso.org/standard/50649.html)
- [Bankgirot OCR reference control](https://www.bankgirot.se/en/services/incoming-payments/bankgiro-receivables/ocr-reference-control/)
- [Bankgirot modulus 10 check digits](https://www.bankgirot.se/globalassets/dokument/anvandarmanualer/10-modul.pdf)

## Related rules

- PAY-22 [Reconcile every payout](https://agentwranglers.se/playbook/payments/payment-events-and-reconciliation/payout-reconciliation.md) in Payments and billing

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/payments/bank-transfers-and-local-methods/structured-payment-reference">Agent Wranglers payments and billing playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Offer each market's local payment methods through your provider](https://agentwranglers.se/playbook/payments/bank-transfers-and-local-methods/local-payment-methods.md)
- Next: [Send bank payouts and refunds with the payee's exact name](https://agentwranglers.se/playbook/payments/bank-transfers-and-local-methods/verification-of-payee.md)
- [Bank transfers and local methods](https://agentwranglers.se/playbook/payments/bank-transfers-and-local-methods.md)
