Generate EDI 999,997,TA1 and CONTRL acknowledgments

An EDI acknowledgment serves as a receipt, to acknowledge that an EDI transaction, or a group of transactions, was received by the remote party. It is pivotal that the communication between business partners is reliable and audit-able, and that the exact status of the transactions is known to both parties at any moment. Without specific acknowledgment of receipt and acceptance, subsequent processing of the transactions is usually delayed until their status is confirmed. By incorporating EDI acknowledgments into your business process, most or all status inquiries can be eliminated and trading partners are automatically notified that their transactions have indeed been received and then confirmed, rejected or partially accepted.

EdiFabric AckMan can be used in conjunction with the framework, to produce technical, functional and implementation acknowledgments. It can be easily attached to the reader, without having an impact on the core process, to analyze transactions on the fly and generate acknowledgments by raising them as events. This allows the consumer application to process the acknowledgments asynchronously if needed and to have full control over the state of the currently read item at any given point.

X12 TA1

The X12 TA1 technical acknowledgment reports the status of the processing of an interchange header and trailer. AckMan raises TA1 automatically and only if they had been requested by the sender (ISA14 was set to a positive number). Every interchange header will be analyzed and if a problem is found a TA1 will be raised immediately. The contents of an interchange will always be processed regardless of its status. Upon reaching the interchange trailer (or improper end of file or a new interchange header) it will be analyzed and TA1 will be raised if not previously raised. Trailer faults do not trigger interchange rejection and are only informational. Duplicate detection can be incorporated by providing an external delegate.

Supported X12 TA1 error codes

X12 997

The X12 997 functional acknowledgment reports the status of the processing of a functional group. AckMan raises 997 only if configured to do so. It analyses each group header, message and group trailer (or the lack of it) to determine the state of all messages as part of the group and the group as part of the interchange. AckMan raises 997 as an event upon reaching a group trailer (or improper end of file or a new group or a new interchange header). Duplicate detection can be turned on to identify if a group is a duplicate within an interchange or if a message is a duplicate within a group.

Supported X12 997 error codes

X12 999

The X12 implementation acknowledgment reports the status of the processing of a functional group for HIPAA transactions. It follows almost the same rules as that of 997, however the structure and codes of the acknowledgment contents are different and follow the 999 rules. AckMan allows you to raise either 997 or 999 acknowledgments, but not both in the same time.

Supported HIPAA 999 error codes


The CONTRL acknowledgment (ACK) serves as both technical and functional acknowledgment for EDIFACT-encoded messages. As a technical acknowledgment, the CONTRL message indicates receipt of an interchange. As a functional acknowledgment, the CONTRL message indicates acceptance or rejection of the received interchange, group, or message, with a list of errors or unsupported functionality.

Supported EDIFACT CONTRL error codes

Interchanges can be identified as duplicates by their unique identifier: Sender code (ISA06) + Receiver code (ISA08) + Control number (ISA13)

For example the unique identifier of the following interchange header:

ISA*00* *00* *ZZ*FROM *ZZ*TO *071214*1406*U*00204*810000263*0*T*>~


FROM + TO + 810000263 = FROMTO810000263

In order to be able to detect duplicate interchanges, the unique identifiers need to be preserved externally (in a database for example). An external delegate can be configured in AckMan, which takes the unique identifier as a string and returns an indicator if a duplicate was found.

Func<string, bool> InterchangeDuplicates

Interchange unique numbers should be considered valid only for a certain amount of time that was agreed with the trading partner. After that time the same control numbers can be assigned again and will not be considered as duplicates.

Duplicate groups within an interchange are identified by the group control number GS06. Duplicate messages within a group are identified by the transaction control number ST02.

Each new acknowledgment is essentially a new EDI document. This means that it needs to be decorated with new control numbers. AckMan allows three modes for control number generation - automatic, custom or a combination of the two. Auto generation will assign subsequent control numbers for each interchange, group and message, starting from 1.

Custom generation allows you to pass in either external delegates or the starting numbers. For example if you want to generate new control numbers automatically, but starting from let's say 99 instead of 1. Upon acknowledgment generation, the context will contain the latest used control number so that the consuming application can update its set of control numbers.

ControlIncrementers(int startingIsa, int startingGs, int startingSt)

ControlIncrementers(Func<int>isaIncrementer, Func<int>gsIncrementer, Func<int> stIncrementer)

In case any of the arguments is omitted AckMan will use the default auto generator starting from 1. For better control over the control numbers it is advised that an external database is maintained to hold the state of the control numbers.

AckSettings allows you to configure how the acknowledgments are generated. It provides the following options:


This parameter sets the control number generation mode as described above. It lets you set the initial values for each of the control numbers (interchange, group or transaction set) or pass in delegates to read these externally. By default it assigns consecutive numbers starting from 1.

AllowPartial (X12 only)

Indicates if a group can be marked (Ak901 or IK901) as partial accepted if not all of the messages were accepted. When this is turned off, groups can only be either accepted or rejected. When this is turned on, a group containing both accepted and rejected messages is marked as partial accepted. Regardless of the flag, if a group contains only rejected messages then it is always marked as rejected.

Ak901ShouldBeP (X12 only)

Indicates the partial accepted code to be either E or P. The default is E.


Indicates if AK2 or UCM segment groups should be generated only if the message is rejected or always.


The acknowledgment handler. It is raised after reaching the end of each group as the EDI document is read along. This is where the acknowledgments will be raised and it's the same handler for all acknowledgment types.

AckVersion (X12 only)

The version of the acknowledgment. This can be X12_997 (004010 compliant), Hipaa_997 (005010 compliant) or Hipaa_999 (005010 compliant). The default is X12_997.


Controls the generation of technical acknowledgment. You can enforce or suppress technical acknowledgments. By default it generates it according to the ISA14 flag (for X12) or UNB9 flag (for EDIFACT).


The message handler. It is raised after reaching the end of each message as the EDI document is read along. The event arguments contain the interchange and group headers and whether the message is valid or duplicated.


Indicates whether to detect duplicate messages within the same group.


Indicates whether to detect duplicate groups within the same interchange.


A delegate to detect duplicate interchanges.

var settings = new AckSettings
    AckHandler = (s, a) =>
        if (a.Message.Name == "TA1")
            // a.AckInterchange is TA1
        if (a.Message.Name == "999")
            var ack = BuildAck(a.Isa, a.Gs, a.Message);
    MessageHandler = (s, a) =>
        if (!a.ErrorContext.HasErrors)
            // do something with the message a.Message
    AckVersion = AckVersions.Hipaa_999,
    Ak901ShouldBeP = true,
    AllowPartial = false,
    GenerateForValidMessages = false,
    GroupDuplicates = true,
    Incrementers = new ControlIncrementers(1, 2, 3),
    InterchangeDuplicates = null,
    TransactionSetDuplicates = true
private static string BuildAck(ISA isa, GS gs, EdiMessage ack)
    var memoryStream = new MemoryStream();
    var writer = new X12Writer(memoryStream, Encoding.Default, Environment.NewLine);
    memoryStream.Position = 0;
    using (var reader = new StreamReader(memoryStream))
        return reader.ReadToEnd();

In order to generate acknowledgments you need to create an instance of AckMan. It takes AckSettings as the only parameter.

var ackMan = new AckMan(settings);

To start generating acknowledgments simply publish every item read by the reader, maintaining the sequence, to AckMan. Acknowledgments will be raised in the handlers and can be offloaded for asynchronous processing.

using (var ediReader = new X12Reader(edi, "YourEdiRulesProject"))
    while (ediReader.Read())

The last step is to complete AckMan once the stream had been read. This step is required to acknowledge EDI documents which are missing closing trailers.


When using AckMan all processing happens within the handlers - all acknowledgments arrive in the AckHandler and all messages arrive in the MessageHandler together with their validation context and interchange\group headers. The reader is only used to read the stream and publish the items to AckMan.

The benefit of utilizing AckMan is not just its ability to generate acknowledgments, but to also provide the messages from the EDI document in the context of their respective groups and interchanges, and to indicate their conformance according to their rules.

The acknowledgment handler provides the generated ack as a typed object in case any additional enhancement is needed. AckMan does not support CTX segments and these need to be manually crafted if required. You are also provided with typed interchange header (ISA) and group header (GS) which can be used for producing the final ack by using X12Writer.