Understanding the ISO 8583 bitmap
The bitmap is the cleverest part of ISO 8583 and the first thing that confuses everyone. Here's how to read one by hand — bit numbering, the secondary bitmap, and a full worked example of 7238000108208000.
If ISO 8583 has one idea worth understanding properly, it’s the bitmap. It’s genuinely clever — a whole message’s table of contents in eight bytes. It’s also where nearly everyone gets stuck the first time, usually because they’re reading the bits in the wrong direction.
Let’s fix that for good.
The problem the bitmap solves
ISO 8583 defines up to 128 numbered fields, but any given message uses only a handful. An authorization carries a card number, an amount, a terminal ID. A network keep-alive carries almost nothing. Sending 128 fields — most of them empty — would be enormous and pointless.
So the message carries a presence map. Before the fields themselves, it puts eight bytes where each bit answers one yes/no question: is field N here? Bit 2 set means field 2 (the PAN) follows. Bit 2 clear means it doesn’t. Sixty-four bits, sixty-four answers.
The one rule people get wrong
Here is the rule that saves you: bits are numbered from 1, left to right, most-significant-bit first.
That means the first bit — the highest bit of the first byte — is field 1, not field 8. If you come from a background where bit 0 is the least-significant bit, your instinct is exactly backwards here. Field 1 is the 0x80 bit of byte one. Field 8 is the 0x01 bit of byte one. Keep that straight and everything else is arithmetic.
Reading one by hand
Take the bitmap from the picture above: 7238000108208000. Sixteen hex digits, which is eight bytes, which is sixty-four bits. Go one byte at a time, expand each to binary, and note which positions are set.
| Byte | Hex | Binary | Fields present |
|---|---|---|---|
| 1 | 72 |
0111 0010 |
2, 3, 4, 7 |
| 2 | 38 |
0011 1000 |
11, 12, 13 |
| 3 | 00 |
0000 0000 |
— |
| 4 | 01 |
0000 0001 |
32 |
| 5 | 08 |
0000 1000 |
37 |
| 6 | 20 |
0010 0000 |
43 |
| 7 | 80 |
1000 0000 |
49 |
| 8 | 00 |
0000 0000 |
— |
Read the first byte carefully, because it’s the template for all of them. 72 is 0111 0010. Position 1 (the leftmost bit) is 0 — no field 1. Positions 2, 3, 4 are 1 — fields 2, 3, 4 present. Positions 5 and 6 are 0. Position 7 is 1 — field 7. Position 8 is 0.
Do that for all eight bytes and you get the full guest list: fields 2, 3, 4, 7, 11, 12, 13, 32, 37, 43, 49. Those are exactly the fields the parser will expect to find, in that order, right after the bitmap.
Bit 1 is special: the secondary bitmap
There’s one exception to “bit N means field N,” and it’s bit 1. Bit 1 doesn’t map to a field — it’s a flag that says a second bitmap follows.
Fields only go up to 64 in a single bitmap. If a message needs a field numbered 65–128, it sets bit 1, and then eight more bytes appear right after the first eight. Those cover fields 65 through 128 with the same left-to-right, MSB-first scheme.
flowchart TD
A["Read first 8 bytes (16 hex digits)"] --> B{"Bit 1 set?"}
B -- "no" --> C["Fields 1 to 64 only. Start reading data elements."]
B -- "yes" --> D["Read 8 more bytes: the secondary bitmap"]
D --> E["Fields 1 to 128 in play. Then read data elements."]
In our example, byte one is 72 = 0111 0010, and the leftmost bit is 0. No secondary bitmap. If it had been F2 (1111 0010), bit 1 would be set and you’d need to grab eight more bytes before touching any field data. Miss that flag and every field afterward parses at the wrong offset — the classic “everything is shifted by eight bytes and nothing makes sense” bug.
Building one is the same trick in reverse
Going the other way is just as mechanical. Start with the set of fields you want to send, turn each field number into a bit position, pack the bits into bytes MSB-first, and — if any field is above 64 — set bit 1 and add the second bitmap. The math is trivial; the bookkeeping is where mistakes creep in, especially around that bit-1 flag and the field ordering.
Which is a good argument for not doing it by hand in production code:
from iso8583sim.core.builder import ISO8583Builder
from iso8583sim.core.types import ISO8583Message
msg = ISO8583Message(
mti="0100",
fields={2: "4111111111111111", 3: "000000",
4: "000000001000", 11: "123456", 41: "TERM0001"},
)
raw = ISO8583Builder().build(msg) # bitmap computed for you, bit 1 handled
You give it the fields; it works out which bits to set, whether a secondary bitmap is needed, and lays everything down in order. Parsing is the mirror image — hand it the bytes and read msg.fields by number, no nibble-counting required.
1. Bits are numbered from 1, left to right, most-significant-bit first. Field 1 is the 0x80 bit of byte one.
2. Bit 1 is not a field — it signals that a secondary bitmap (fields 65–128) follows.
3. The bitmap is a hex string on the wire; expand each digit to four bits to read it.
That’s the whole thing. The bitmap looks like line noise until you know it’s just a checklist read MSB-first — and then 7238000108208000 reads as plainly as a sentence. If you want the wider context of where the bitmap sits in a message, start with ISO 8583 explained; if you’d rather stop decoding hex by hand entirely, that’s what the parser is for.