Need help setting up or customizing iso8583sim? Email us at [email protected] Get in touch →

ISO 8583, explained: the format behind every card payment

A plain-English tour of ISO 8583 — the MTI, the bitmap, data elements, and the request/response flow. The 40-year-old message format still running the world's card rails, minus the spec-reading headache.

Tap your card at a coffee shop and, before you’ve pulled it away from the reader, a small binary message is already racing across the planet toward the bank that issued it. That message is almost certainly ISO 8583. It’s the lingua franca of card payments — the thing ATMs, point-of-sale terminals, payment switches, and the card networks all agree to speak.

Here’s the strange part: the standard was first published in 1987. It predates the web. And it is still, today, carrying the overwhelming majority of the world’s card authorizations. If you work anywhere near payments long enough, you will meet it. So let’s take the mystery out of it.

What it actually is

ISO 8583 is a format for financial transaction messages. “Please authorize $4.50 on this card.” “Approved.” “Actually, reverse that.” Each of those is one message, and every message has the same three-part shape:

  1. a message type indicator (MTI) — what kind of message this is,
  2. one or more bitmaps — a map of which fields are present,
  3. the data elements — the fields themselves: card number, amount, and so on.

That’s it. Everything else is detail. Get those three ideas straight and the rest falls into place.

The MTI: four digits that set the scene

Every message opens with a four-digit message type indicator. It’s not a random ID — each digit means something specific.

0
Version1987
1
ClassAuthorization
0
FunctionRequest
0
OriginAcquirer
The four digits of MTI 0100 — an authorization request, sent by the acquirer.

Read 0100 left to right: 1987 version, authorization class, a request, from the acquirer side. Flip the third digit to 1 and you get 0110 — the response to that request. This pairing is the whole rhythm of the protocol: a request goes out, a response comes back with the same class and a function digit bumped by one. Learn a handful of these — 0100/0110 (authorization), 0200/0210 (financial), 0400/0410 (reversal), 0800/0810 (network management) — and you can read the intent of most traffic at a glance.

The bitmap: a checklist of what’s inside

ISO 8583 has 128 possible data elements, and no message uses all of them. An authorization needs a card number and an amount; a network echo test needs almost nothing. So instead of sending empty placeholders, the message carries a bitmap: 64 bits where bit N is set to 1 if field N is present.

A primary bitmap. Each lit cell is a field that's present — here fields 2, 3, 4, 11, 41, and friends. Hex: 72 38 00 01 08 20 80 00.

It’s a beautifully compact idea, and it’s also the single thing that trips up newcomers most. If you’ve ever stared at 7238000108208000 wondering how anyone reads it, that’s the bitmap — and it gets its own full write-up, because it deserves one.

Data elements: the actual payload

After the bitmap come the fields the bitmap promised. They’re numbered, not named, and you’re expected to know what each number means. A few you’ll see constantly:

Field Name Example
DE 2 Primary account number (PAN) 4111111111111111
DE 3 Processing code 000000 (purchase)
DE 4 Amount, in minor units 000000001000 ($10.00)
DE 11 System trace audit number (STAN) 123456
DE 39 Response code 00 (approved), 51 (insufficient funds)
DE 41 Terminal ID TERM0001
DE 55 EMV / chip data (TLV) 9F26080123...

Notice DE 4: amounts are in minor units with no decimal point. 000000001000 is ten dollars, not a thousand. Get that wrong and you’ll authorize a transaction 100× too large — a mistake that has genuinely shipped to production more than once.

How a transaction flows

A single purchase isn’t one message; it’s a relay. The terminal hands its request to an acquirer (the merchant’s bank), which routes it through the card network (Visa, Mastercard, and so on) to the issuer (the cardholder’s bank). The issuer makes the decision and the answer walks back down the same chain.

sequenceDiagram
    autonumber
    participant T as Terminal
    participant A as Acquirer
    participant N as Card network
    participant I as Issuer
    T->>A: 0100 (auth request)
    A->>N: 0100
    N->>I: 0100
    I-->>N: 0110 · DE39 = 00
    N-->>A: 0110
    A-->>T: 0110 (approved)
An authorization round-trip. The 0100 request climbs to the issuer; the 0110 response carries the verdict — response code 00, approved — back down.

Each hop is a separate ISO 8583 message, and each party may add, translate, or strip fields on the way through. That “may translate” is where a lot of real-world pain lives, which brings us to the honest part.

Why it has a reputation

ISO 8583 is a standard, but treating it as one interoperable spec will get you hurt. In practice:

  • Everyone has a dialect. The ISO document defines the field catalog and the mechanics, but Visa, Mastercard, and every domestic switch layer their own rules on top — which fields are mandatory, how they’re encoded, what goes in the bilateral “reserved” fields. A message that’s valid for one network may be nonsense to another.
  • Encodings vary. Fields can be ASCII, EBCDIC, BCD-packed, or binary depending on the implementation. Length prefixes (LLVAR, LLLVAR) add another layer of “it depends.”
  • The versions coexist. There are three — 1987, 1993, and 2003 — and despite 2003 being the newest, 1987 is still the one you’ll meet most often. Newer isn’t deployed-er.

None of this is insurmountable. It’s just why “read the message” is rarely as simple as it sounds, and why having a tool that already knows the mechanics saves you days.

Working with it, without the headache

This is the part where a library earns its keep. Here’s the same round-trip from above, parsed:

from iso8583sim.core.parser import ISO8583Parser

parser = ISO8583Parser()
msg = parser.parse(raw)          # raw: the on-the-wire message

msg.mti                          # '0100'  — authorization request
msg.fields[2]                    # '4111111111111111'  — PAN
msg.fields[4]                    # '000000001000'  — $10.00

No manual bitmap arithmetic, no counting nibbles. Parse it, read the fields by number, and get on with whatever you were actually building. If you want to go the other direction — hand-build a message, validate it against a network’s rules, or generate realistic test traffic — that’s the quick start, and simulating traffic gets its own article.

ISO 8583 isn’t elegant, and it isn’t going anywhere. Forty years in, it’s still the format the money moves on. Understanding its three parts — the MTI, the bitmap, the data elements — is most of the battle. The rest is just knowing which dialect you’re speaking.

Try it yourself

Everything here works with the open-source library. One line to install.

$pip install iso8583sim
← All articles