{} MT940 to JSON

Turn an MT940 statement into structured JSON — every tag parsed, including the :86: narrative broken into SEPA fields. Converted in your browser; nothing is uploaded.

Your statement stays on your device. This page reads the file with JavaScript in your browser. Nothing is uploaded, and it keeps working with your network disconnected.
📄
Drop your MT940 file here
or click to browse — .sta, .mt940, .940, .txt
MT940 & MT942 · .sta .mt940 .940 .txt · up to 20 MB
…or paste the statement text instead

MT940 as structured JSON

MT940 is a flat stream of colon-delimited tags with a grammar that predates JSON by decades. This tool turns it into the object you actually want to work with: statements, balances, transactions, and a fully decoded :86: narrative.

Everything runs in your browser, which makes it usable with real statements rather than sanitised samples.

Nothing is uploaded. The parser is JavaScript on your machine — the same parser that drives the MT940 viewer.

The output shape

The top level tells you what kind of file you gave it and what was found:

  • okfalse plus an error string if the file couldn't be read
  • type"MT940" or "MT942", detected from the tags present
  • bank — which :86: dialect was detected: german, tagged or generic
  • statements[] — one entry per :20: block
  • totals — statement count, transaction count, credit and debit sums, currency
  • warnings[] — human-readable notes: broken balances, missing pages, skipped lines, an encoding fallback

Inside a statement

  • reference, related_reference, statement_no, sequence_no, created_at
  • accountraw, iban, identifier, bic
  • opening, closing, available, forward[] — each with dc, date, currency, amount and a signed value so you don't have to apply the sign yourself
  • sum_debits / sum_credits — the MT942 :90D: / :90C: totals
  • totals and reconciliation — with status ok, mismatch or unknown, plus expected and actual figures
  • transactions[]

Inside a transaction

  • value_date and entry_date as ISO YYYY-MM-DD — the two-digit SWIFT year and the bare MMDD entry date are resolved for you, including across a year boundary
  • amount (always positive), is_credit, signed, dc, is_reversal, funds_code, decimals
  • type_code and type_labelNTRF plus "Transfer"
  • customer_ref, bank_ref, supplementary
  • narrative — the decoded :86:, and narrative_raw for the original text

The decoded narrative

All three dialects produce the same object, so your code doesn't branch per bank: dialect, text, purpose, counterparty (name / iban / bic), eref, mref, cred, booking_text, gvc, gvc_label and a labelled fields map holding everything else the bank sent.

Details worth knowing

Amounts are numbers, not strings

SWIFT writes 1234,56. It comes out as 1234.56, and decimals records how many decimal places the file actually used, so you can spot sub-cent currencies.

Reconciliation is computed in cents

Opening plus credits minus debits is compared to the stated closing balance in integer minor units. That's why a clean file never reports a phantom one-cent mismatch. Where amounts have more than two decimals the status is honestly unknown rather than a guess.

Unknown codes stay empty

If a SWIFT type code or German GVC isn't in the lookup table, type_label and gvc_label are empty strings. The parser never invents a plausible-sounding description for a code it doesn't know.

Common use cases

  • Feed a bookkeeping integration without writing an MT940 grammar yourself
  • Build a fixture for tests from a real statement, then redact it
  • Reconcile payments against orders by end-to-end reference
  • Inspect exactly which SEPA fields a bank populates before coding against them
  • Diff two statement exports to find what changed

Security & privacy

  • Converted on your device — no upload, no request, nothing logged
  • Works offline once loaded
  • No signup and no conversion limit

Frequently Asked Questions

No. Parsing happens in your browser. Load the page, go offline, convert — it works.
Not currently. This page is the parser — the JSON you download here is the parser's complete output, so it's a fair way to check the shape before you build against it.
Yes, and that's most of the work. German ?nn subfields with EREF+/MREF+/CRED+/SVWZ+ prefixes, Dutch /NAME/-style slash tags, and plain free text all produce the same object shape, so you don't branch per bank.
The entry date is optional in :61:. When the bank omits it, entry_date falls back to the value date so the field is always present and always a date.
You get ok: false and an error explaining what was expected. Individual unreadable :61: lines don't abort the parse — they're skipped and reported in warnings, so a partly-corrupt file still gives you the rest.