ISO 8583: Message Structure

ASSI Avatar

If you’ve ever worked on a payment switch, POS terminal integration, or bank-to-bank transaction processing, you’ve probably encountered the mysterious and often misunderstood beast known as ISO 8583. When I first touched it in one of our payment switch projects, I remember being confused by TPDU headers, LLVAR fields, and different message formats. It felt like decoding hieroglyphs. But over time (and with a few late nights), it started to make sense.

So in this blog, I want to walk through the ISO 8583 message structure based on my actual experience. I’ll break down the essentials, including TPDU, ASYNC vs SYNC structures, and how message flow through TCP/IP. By the end of this, you should be able to read and construct ISO messages with confidence.

If you’re new to ISO 8583 or unsure what it is, I recommend checking out my previous post: Introduction to ISO-8583

Basic Message Structure

An ISO 8583 message is made up of several parts:

Bash
[Message Header][Message Type Identifier][Bitmap][Data Elements]
Message Header

This part is optional and can include transport protocol headers such as TPDU or custom headers. Some financial institution may also require a message length prefix (usually 2 bytes in hex format) at the very start of the message. This length prefix indicates the number of bytes that follow, which is helpful when reading from a TCP/IP stream.

Whether or not you need to include the length header depends entirely on the financial institution or acquirer you’re working with. Some require it, others don’t, always refer to their integration specs.

Message Type Identifier (MTI)

This is a 4-digit number that tells you what kind of message it is. If you want to know more about MTI, I have a separate blog with a full discussion about the Message Type Indicator or MTI.

Below is the sample of MTI

MTIMeaning
0100Authorization Request
0110Authorization Response
0200Financial Request
0210Financial Response
Bitmap

A bitmap tells you which Data Elements are present in the message. It can be:

  • Primary (fields 1 – 64)
  • Secondary (fields – 65 – 128)
Data Elements

These are the actual data fields like card number (DE2), processing code (DE3), amount (DE4), etc. In ISO 8583, Data Fields or Data Elements are defined with both a content type and a length format. These fields fall under two broad categories:

1. Field Categories

ISO 8583 fields are grouped based on whether they are commonly used across networks or customized for specific implementations.

  • Standard Fields: These have predefined meanings and consistent formats across most networks. Common examples include:
    • DE2: Primary Account Number
    • DE3: Processing Code
    • DE4: Transaction Amount
    • DE7: Transmission Date and Time
    • DE11: Systems Trace Audit Number (STAN)
    • DE12: Local Transaction Time
    • DE37: Retrieval Reference Number
    • DE39: Response Code
    • DE42: Card Acceptor Terminal ID
    • DE49: Transaction Currency Code
  • Network-Specific Fields: These are defined by specific card networks or acquirers and often vary across integrations. Example include:
    • DE48: Additional Data – Private (often varies by acquirer)
    • DE62: Reserved for Private Use
    • DE63: Reserved for Private Use
    • DE90: Original Data Elements (used in some reversal messages, format may vary)
    • DE126-127: Private fields often defined per network or terminal specification. These are defined by individual card schemes or financial institutions and may vary from one network to another.
2. Data Element Format

Each data element in ISO 8583 follows a defined format, which includes content type and length handling rules.

  • Content Type – The content of each data element is defined by a type code:
    • n: Numeric digits only (0-9)
    • a: Alphabetic characters (A-Z, a-z)
    • s: Special characters
    • an: Alphanumeric (A-Z, a-z, 0-9)
    • ans: Alphanumeric and special characters
    • anp: Alphanumeric with padding characters
  • Length Type – This defines how the field’s size is determined, whether it has a fixed number of characters or uses a length prefix to indicate dynamic length.
    • Fixed Length: The data field has a set number of characters.
    • Variable Length: The actual length is dynamic and preceded by a length indicator:
      • LLVAR: Up to 99 characters (2-digit length prefix)
      • LLLVAR: Up to 999 characters (3-digit length prefix)
3. Sample Data Elements

These examples illustrate how different types of fields are defined in terms of format, length, and content type based on ISO 8583 standards.

  • PrimaryAccountNumber_002: LLVAR, numeric (Type: n, Length: LL)
  • TransactionAmount_004: Fixed length, numeric (Type: n, Length: 12)
  • Track2Data_035: LLVAR, alphanumeric + special (Type: ans, Length: LL)
4. Field Encoding

ISO 8583 messages are encoded to optimize performance and bandwidth. Common encoding techniques include:

  • ASCII or EBCDIC: Typically used for textual data
  • Packed BCD (Binary-Coded Decimal): Common for numeric fields, where each digit occupies 4 bits
  • Binary: For compact representation of fixed-length integers or control fields

These encoding techniques are crucial to understand, especially when you’re debugging raw ISO messages. One common scenario you’ll encounter is handling variable-length fields, such as LLVAR. These fields use a length prefix to indicate how much data follows, and it’s something you’ll often need to manually construct or parse in your application logic.

Here’s a quick refresher on how to build an LLVAR field in C#:

C#
string GetLLVAR(string input)
{
    string length = input.Length.ToString().PadLeft(2, '0');
    return length + input;
}

// Example
string cardHolderName = "JUAN DELA CRUZ";
Console.WriteLine(GetLLVAR(cardHolderName));
Bash
Outputs: 14JUAN DELA CRUZ

This technique is helpful when you need to prepare a field before assembling the full ISO 8583 message.

TPDU (Transport Protocol Data Unit)

In ISO 8583, the TPDU (Transport Protocol Data Unit) is part of the message header used to route messages at the network level. It’s usually 5 bytes and consists of:

  • Destination Address
  • Source Address
  • Control Information
Example TPDU: 6000030000

In our project, this was crucial for terminal routing, especially when handling multiple acquirers. We configured the TPDU differently depending on the acquirer to ensure that the message reached the correct host system.


ASYNC vs. SYNC (X.25) vs. TCP/IP Message Structures

There are two common transmission structures used in the real world: ASYNC and SYNC (X.25). Understanding how they wrap your ISO message will help you debug connectivity issues and make integration with hosts, especially legacy systems, much smoother.

ASYNC Message Structure (e.g., RS232 or TCP/IP)

This structure is commonly used in older terminals or simple TCP/IP-based systems. It’s lightweight and straightforward—but that also means it’s more prone to ambiguity. In one of our implementations, we had to deal with parsing issues just because the message didn’t include a length header, which made us rely on timeouts and data framing logic to detect message boundaries.

This structure generally looks like:

Bash
[TPDU][ISO Message]

In many older implementations, there’s no length indicator. This can make parsing more challenging—you often rely on timeouts or specific end-of-message markers instead.

SYNC (X.25) Message Structure

This structure is more formal and typically used with synchronous protocols like X.25. It usually follows this layout:

Bash
[Length][TPDU][ISO Message]
  • Length: 2 bytes (or sometimes 4 ASCII characters) indicating the size of the message.
  • TPDU: 5 bytes for transport routing.

This structure is more reliable when dealing with predictable, stream-based protocols.

Sample Code to Read a Length-Header-Based Message

C#
byte[] ReadMessage(Stream stream)
{
    byte[] lengthHeader = new byte[2];
    stream.Read(lengthHeader, 0, 2);

    int length = (lengthHeader[0] << 8) | lengthHeader[1];
    byte[] message = new byte[length];
    stream.Read(message, 0, length);

    return message;
}
TCP/IP Message Structure

Most modern ISO 8583 systems run over TCP/IP. The structure typically is:

Bash
[Length][TPDU][ISO Message]

Here, the length prefix helps the receiving system know how many bytes to read. This solved a lot of our problems with incomplete reads and stream parsing.

Example (Hex Representation):

Bash
00 82 60 00 03 00 00 02 00 B2 30 30 30...
  • 00 82 = Message length (130 bytes)
  • 60 00 03 00 00 = TPDU
  • 02 00 B2 30 30 30... = ISO Message

Tips From the Field

  • Bitmap parsing is crucial – Mistakes in interpreting the bitmap lead to incorrect field mapping.
  • Use hex viewers – Tools like HxD or Wireshark helped me dissect real ISO messages.
  • Logging raw messages – Always log both hex and ASCII versions of the request/response.
  • Write modular parsers – Separate header, bitmap, and DE parsing logic.

ISO 8583 can be intimidating at first, but once you understand the message flow, field formats, and structures like TPDU and bitmaps, it gets easier. Whether you’re building a terminal, a payment switch, or working with banks, knowing these internals helps you debug and integrate faster.

Sources & References: