Introduction
HL7 messages often contain multiple repeating segments such as NTE, AL1, OBX, ZTX, DG1, and others. These segments sometimes require individual processing and routing to different downstream systems. This technical paper introduces a configurable template designed to automate the splitting of these repeating HL7 segments, improving message handling and integration efficiency.
This template offers flexibility by allowing users to either send the message as-is with the required logic applied or split the message based on customizable settings defined within the business process configuration. Importantly, this approach supports reuse across various use cases and HL7 schemas.
Please note that you may need to update or modify a few lines of code depending on your HL7 schema or any custom schema you use. The source message must conform to your message schema.
Key configurable parameters include:
- Message type selection
- Identification of repeating segments
- Segment group definitions
- Unique identifier settings
Highlights:
- The source message can contain multiple repeating segments such as NTE, AL1, OBX, etc.
- You can send the entire message without splitting it, applying only your logic, to downstream systems.
- Alternatively, you can split the message according to your instructions and send the segments separately to one or multiple downstream systems as needed.
This process is designed to split any repeating HL7 message segment and route it to the chosen downstream systems. It functions as a reusable template, requiring little to no code modification to adapt to your specific needs.
The process provides dynamic configuration settings within the business process setup, allowing users to customize message splitting as needed. The main settings are detailed in Table 1 below:
Table 1: Process Settings – Configurations
Usage Instructions
- Copy the provided code and create a new business process in the HealthConnect Management Portal using the code class.
- Navigate to Process → Settings, then select TargetConfigurations.

- Choose the operations and target systems where you want to send the message.
- Configure all settings as specified in the table above according to your requirements and apply the changes.
- Restart your process to activate the new configuration.
TESTING:
Screenshot 1: Management Portal showing sending the entire source HL7 message and sending repeating segments to three different systems.

Select à Process à Setting and complete all the configuration according to your requirements.
Screenshot 2.a: Process settings configuration screen.

If you need more information about any configuration, simply click the title for detailed explanations.
Screenshot 2.b: Supporting information screen.

Examples of Source and Target Messages:
Screenshot 3: Source message containing 5 repeating ZTX segments.

Screenshot 4.a: Target system configuration sending the source message as-is and splitting the ZTX segments individually to operations.

Screenshot 4.b: Target system sending the source message as-is and splitting only the last ZTX segment to operations.

Another example where only split messages are sent, without sending the original full message first:
Screenshot 5: Source message with two ZTX segments split into two separate messages, each containing one ZTX segment.
Screenshot 6: Source Message

Screenshot 7.a: Target message containing the first ZTX segment.

Screenshot 7.b: Target message containing the second ZTX segment.

Additional Example with Different Configurations
In this example, the source message contains multiple repeating NTE segments, where each NTE segment is part of the group path: PIDgrpgrp().ORCgrp().OBXgrp.
The process is configured to split and send each NTE segment individually to the target system. The resulting target messages are shown below:
- Screenshot 10.a – First NTE segment
- Screenshot 10.b – Second NTE segment
- Screenshot 10.c– Third NTE segment
Screenshot 8: Process Configurations

Screenshot 9: Source message

Screenshot 10.a: Target message – First NTE segment

Screenshot 10.b: Target message – Second NTE segment

Screenshot 10.c: Target message – Third NTE segment
.png)
CODE
Class SANJIB.Process.SegmentSplitAndSendMessage Extends Ens.BusinessProcess [ ClassType = persistent ]
{
Property MessageType As %String(MAXLEN = 10)
Property MultipleSegmentName As %String(MAXLEN = 20)
Property IsRepeating As %String(VALUELIST = ",Y,N") [ InitialExpression = "Y", Required ]
Property SegmentRepeatingGrp As %String(VALUELIST = ",Y,N") [ InitialExpression = "N", Required ]
Property MultipleSegGrpPath As %String(VALUELIST = ",NA,PIDgrp,MRGgrp,PRG1grp,IN1grp,ORCgrp(1).OBRuniongrp,PIDgrpgrp(1).ORCgrp(1).OBXgrp,The following requires more testing — do not select or use in production!,ORCgrp(1).OBRuniongrp.OBXgrp,RGSgrp(1).AILgrp,RGSgrp(1).AIGgrp,RGSgrp(1).AISgrp") [ InitialExpression = "NA", Required ]
Property SegmentNameForUniqueID As %String(MAXLEN = 5)
Property SegmentDetails As %String(MAXLEN = 50)
Property SegmentFieldNo As %String(MAXLEN = 2)
Property SeperationUniqueID As %String(MAXLEN = 2)
Property TargetConfigurations As Ens.DataType.ConfigName(MAXLEN = 100) [ Required ]
Parameter SETTINGS = "MessageType,MultipleSegmentName,IsRepeating,SegmentRepeatingGrp,MultipleSegGrpPath,SegmentNameForUniqueID,SegmentDetails,SegmentFieldNo,SeperationUniqueID,TargetConfigurations:Basic:selector?multiSelect=1&context={Ens.ContextSearch/ProductionItems?targets=1&productionName=@productionId}"
Method OnRequest(pRequest As EnsLib.HL7.Message, Output pResponse As Ens.Response) As %Status
{
S tSC = $$$OK
S (msgType,mulSeg,isRepeat,segRepGrp,mulSegGrpPath,segNameUID,segFieldNo,sepUID,tgtConfig)=""
S msgType =..MessageType
S mulSeg=..MultipleSegmentName
S isRepeat=..IsRepeating
S segRepGrp=..SegmentRepeatingGrp
S mulSegGrpPath=..MultipleSegGrpPath
S segNameUID=..SegmentNameForUniqueID
S segDetail=..SegmentDetails
S segFieldNo=..SegmentFieldNo
S sepUID=..SeperationUniqueID
S tgtConfig=..TargetConfigurations
K messageType S messageType=pRequest.GetValueAt("MSH:9")
K cpyMsg S cpyMsg = pRequest.%New()
S cpyMsg =pRequest.%ConstructClone()
S (segIdx,totSegment)=0
Try
{
#dim totSegment=pRequest.SegCount
F i=1:1:totSegment
{
K segName S segName = pRequest.GetSegmentAt(i).Name
I (segName = mulSeg)
{
S segIdx=i
Q
}
}
I ((msgType="" ) || (mulSeg="")||(isRepeat="")|| (segRepGrp=""))
{
S tSC=$system.Status.Error(5001,"Empty fields. Please check the MessageType, MultipleSegmentName, IsRepeating, SegmentRepeatingGrp,multipleSegGrpPath in the Process Settings at the Management Portal.")
}
elseif messageType=msgType
{
I ((isRepeat= "Y") && (segRepGrp="N")&& (mulSegGrpPath="NA") && (segIdx >0))
{
S tSC=..ProcessMessageFromNoRepeatGroup(cpyMsg,msgType,mulSeg,isRepeat,segRepGrp,segNameUID,segDetail,segFieldNo,sepUID,segIdx,tgtConfig)
}
elseif ((isRepeat= "Y") && (segRepGrp="Y") && (mulSegGrpPath="NA") && (segIdx >0))
{
S tSC=..ProcessMessageFromRepeatingGroup(cpyMsg,msgType,mulSeg,isRepeat,segRepGrp,segNameUID,segDetail,segFieldNo,sepUID,segIdx,tgtConfig)
}
elseif ((isRepeat = "Y") && (mulSegGrpPath '="") && (segIdx >0))
{
S tSC=..ProcessMessageFromRepeatingGroupPath(cpyMsg,msgType,mulSeg,isRepeat,segRepGrp,mulSegGrpPath,segNameUID,segDetail,segFieldNo,sepUID,segIdx,tgtConfig)
}
else
{
S tSC=..ProcessMessage(pRequest,tgtConfig)
}
}
}
catch ErException
{
S tSC=ErException.AsStatus()
$$$TRACE("Exception :"_tSC)
}
Q tSC
}
Method ProcessMessageFromNoRepeatGroup(ByRef mRequest As EnsLib.HL7.Message, msgType As %String, mulSeg As %String, isRepeat As %String, segRepGrp As %String, segNameUID As %String, segDetail As %String, segFieldNo As %Integer, sepUID As %String, segIdx As %Integer, tgtConfig As %String) As %Status
{
S tSC = $$$OK
Try
{
K totSeg
#dim totSeg = mRequest.SegCount
S dataSets=..GetDataSets(mRequest,segNameUID,segDetail,segFieldNo)
K flag S msgUID=$P(dataSets,":",1)
K flag S flag=$P(dataSets,":",2)
I sepUID="" S sepUID="-"
K totRepSeg S totRepSeg=mRequest.GetValueAt(mulSeg_"(*)")
I (totRepSeg >1)
{
F j=1:1:totRepSeg
{
K newMsg S newMsg=##class(EnsLib.HL7.Message).%New()
S newMsg.DocType=mRequest.DocType
I ((segNameUID="MSH") && (flag="G"))
{
D newMsg.SetSegmentAt(mRequest.FindSegment("MSH"),1)
D newMsg.SetValueAt(msgUID_sepUID_j,"MSH:"_segFieldNo)
}
else
{
D newMsg.SetSegmentAt(mRequest.FindSegment("MSH"),1)
}
F k=1:1:totSeg
{
K segName S segName = mRequest.GetSegmentAt(k).Name
I (segName '= "MSH") && (segName '= mulSeg)
{
K getSegment S getSegment=mRequest.FindSegment(segName,,.sc)
I $ISOBJECT(getSegment)
{
I ((segName=segNameUID) && (flag ="G"))
{
D newMsg.AppendSegment(getSegment)
D newMsg.SetValueAt(msgUID_sepUID_j,segDetail_":"_segFieldNo)
}
else
{
D newMsg.AppendSegment(getSegment)
}
}
else
{
s tSC=$system.Status.Error(5001,"Not found "_segName_"Segment")
}
}
}
S addSeg=mRequest.GetSegmentAt(mulSeg_"("_j_")")
D newMsg.InsertSegmentAt(addSeg,segIdx)
$$$QuitOnError(..ProcessMessage(newMsg,tgtConfig))
K addSeg
}
}
else
{
S tSC=..ProcessMessage(mRequest,tgtConfig)
}
}
catch ErException
{
S tSC=ErException.AsStatus()
$$$TRACE("Exception :"_tSC)
}
Q tSC
}
Method ProcessMessageFromRepeatingGroup(ByRef pRequest As EnsLib.HL7.Message, msgType As %String, mulSeg As %String, isRepeat As %String, segRepGrp As %String, segNameUID As %String, segDetail As %String, segFieldNo As %Integer, sepUID As %String, segIdx As %Integer, tgtConfig As %String) As %Status
{
S tSC = $$$OK
Try
{
K totSeg
#dim totSeg = pRequest.SegCount
S dataSets=..GetDataSets(pRequest,segNameUID,segDetail,segFieldNo)
K flag S msgUID=$P(dataSets,":",1)
K flag S flag=$P(dataSets,":",2)
I sepUID="" S sepUID="-"
K totRepSeg S totRepSeg=pRequest.GetValueAt(mulSeg_"grp(*)")
I (totRepSeg >1)
{
F j=1:1:totRepSeg
{
K newMsg S newMsg=##class(EnsLib.HL7.Message).%New()
S newMsg.DocType=pRequest.DocType
I ((segNameUID="MSH") && (flag="G"))
{
D newMsg.SetSegmentAt(pRequest.FindSegment("MSH"),1)
D newMsg.SetValueAt(msgUID_sepUID_j,"MSH:"_segFieldNo)
}
else
{
D newMsg.SetSegmentAt(pRequest.FindSegment("MSH"),1)
}
F k=1:1:totSeg
{
K segName S segName = pRequest.GetSegmentAt(k).Name
I (segName '= "MSH") && (segName '= mulSeg)
{
K getSegment S getSegment=pRequest.FindSegment(segName,,.sc)
I $ISOBJECT(getSegment)
{
I ((segName=segNameUID) && (flag ="G"))
{
D newMsg.AppendSegment(getSegment)
D newMsg.SetValueAt(msgUID_sepUID_j,segDetail_":"_segFieldNo)
}
else
{
D newMsg.AppendSegment(getSegment)
}
}
else
{
S tSC=$system.Status.Error(5001,"Not found "_segName_"Segment")
}
}
}
S addSeg=pRequest.GetSegmentAt(mulSeg_"grp("_j_")."_mulSeg)
D newMsg.InsertSegmentAt(addSeg,segIdx)
$$$QuitOnError(..ProcessMessage(newMsg,tgtConfig))
K addSeg
}
}
else
{
S tSC=..ProcessMessage(pRequest,tgtConfig)
}
}
catch ErException
{
S tSC=ErException.AsStatus()
$$$TRACE("Exception :"_tSC)
}
Q tSC
}
Method ProcessMessageFromRepeatingGroupPath(ByRef pRequest As EnsLib.HL7.Message, msgType As %String, mulSeg As %String, isRepeat As %String, segRepGrp As %String, mulSegGrpPath As %String, segNameUID As %String, segDetail As %String, segFieldNo As %Integer, sepUID As %String, segIdx As %Integer, tgtConfig As %String) As %Status
{
S tSC = $$$OK
Try
{
K totSeg
#dim totSeg = pRequest.SegCount
S dataSets=..GetDataSets(pRequest,segNameUID,segDetail,segFieldNo)
K flag S msgUID=$P(dataSets,":",1)
K flag S flag=$P(dataSets,":",2)
I sepUID="" S sepUID="-"
S mulSegPathType=0
I mulSegGrpPath="ORCgrp(1).OBRuniongrp"
{
S totRepSeg=pRequest.GetValueAt(mulSegGrpPath_"."_mulSeg_"(*)")
S mulSegPathType=1
}
else
{
S totRepSeg=pRequest.GetValueAt(mulSegGrpPath_"(1)."_mulSeg_"(*)")
S mulSegPathType=2
}
I (totRepSeg >1)
{
F j=1:1:totRepSeg
{
K newMsg S newMsg=##class(EnsLib.HL7.Message).%New()
S newMsg.DocType=pRequest.DocType
I ((segNameUID="MSH") && (flag="G"))
{
D newMsg.SetSegmentAt(pRequest.FindSegment("MSH"),1)
D newMsg.SetValueAt(msgUID_sepUID_j,"MSH:"_segFieldNo)
}
else
{
D newMsg.SetSegmentAt(pRequest.FindSegment("MSH"),1)
}
F k=1:1:totSeg
{
K segName S segName = pRequest.GetSegmentAt(k).Name
I (segName '= "MSH") && (segName '= mulSeg)
{
K getSegment S getSegment=pRequest.FindSegment(segName,,.sc)
I $ISOBJECT(getSegment)
{
I ((segName=segNameUID) && (flag ="G"))
{
D newMsg.AppendSegment(getSegment)
D newMsg.SetValueAt(msgUID_sepUID_j,segDetail_":"_segFieldNo)
}
else
{
D newMsg.AppendSegment(getSegment)
}
}
else
{
s tSC=$system.Status.Error(5001,"Not found "_segName_"Segment")
}
}
}
I mulSegPathType=1
{
S addSeg=pRequest.GetSegmentAt(mulSegGrpPath_"."_mulSeg_"("_j_")")
}
I mulSegPathType=2
{
S addSeg=pRequest.GetSegmentAt(mulSegGrpPath_"(1)."_mulSeg_"("_j_")")
}
D newMsg.InsertSegmentAt(addSeg,segIdx)
$$$QuitOnError(..ProcessMessage(newMsg,tgtConfig))
K addSeg
}
}
else
{
S tSC=..ProcessMessage(pRequest,tgtConfig)
}
}
catch ErException
{
S tSC=ErException.AsStatus()
$$$TRACE("Exception :"_tSC)
}
Q tSC
}
Method GetDataSets(ByRef pRequest As EnsLib.HL7.Message, segNameUID As %String, segDetail As %String, segFieldNo As %Integer, pOutput As %Integer = 0, flag As %String = "R") As %Status
{
S tSC=$$$OK
Try
{
I $ISOBJECT(pRequest)
{
K msgUID
I (segNameUID '="" && segDetail '= "" && segFieldNo '="")
{
S msgUID = pRequest.GetValueAt(segDetail_":"_segFieldNo)
I (msgUID =""){S msgUID=0}
S flag="G"
S pOutput=msgUID
}
}
else
{
S tSC=$system.Status.Error(5001,"Not a valid HL7 message!")
}
}
catch Exception
{
S tSC=Exception.AsStatus()
}
Q pOutput_":"_flag
}
Method ProcessMessage(pMessage As EnsLib.HL7.Message, TargetList As %String) As %Status
{
S tSC=$$$OK
Try
{
I $ISOBJECT(pMessage)
{
For i=1:1:$L(TargetList,",")
{
S tSC=..SendRequestAsync($P(TargetList,",",i),pMessage,0)
}
}
}
catch ErException
{
S tSC=ErException.AsStatus()
}
Q tSC
}
Method OnResponse(request As Ens.Request, ByRef response As Ens.Response, callrequest As Ens.Response, callresponse As Ens.Response, pCompletionKey As %String) As %Status
{
Quit $$$OK
}
Storage Default
{
<Data name="SegmentSplitAndSendMessageDefaultData">
<Subscript>"SegmentSplitAndSendMessage"</Subscript>
<Value name="1">
<Value>MessageType</Value>
</Value>
<Value name="2">
<Value>MultipleSegmentName</Value>
</Value>
<Value name="3">
<Value>IsRepeating</Value>
</Value>
<Value name="4">
<Value>SegmentRepeatingGrp</Value>
</Value>
<Value name="5">
<Value>MultipleSegGrpPath</Value>
</Value>
<Value name="6">
<Value>SegmentNameForUniqueID</Value>
</Value>
<Value name="7">
<Value>SegmentDetails</Value>
</Value>
<Value name="8">
<Value>SegmentFieldNo</Value>
</Value>
<Value name="9">
<Value>SeperationUniqueID</Value>
</Value>
<Value name="10">
<Value>TargetConfigurations</Value>
</Value>
</Data>
<DefaultData>SegmentSplitAndSendMessageDefaultData</DefaultData>
<Type>%Storage.Persistent</Type>
}
}
You can also download the code from the GitHub
I hope this template will help streamline your HL7 message processing. If you have any questions or need further assistance, please feel free to ask.
Thank you.