Monday, 7 September 2026

SQL connection timeouts in Integration Host

I had a Database Query ready to go. The SQL was the same statement I already trusted. As soon as the query ran, there was a timeout. Integration Host showed the error clearly. The workflow stopped. The rows never came back.

Firstly, what does that actually mean?

It means the wait is not a workflow setting. There is no timeout box in the UI. Database Query, Database Receiver, the SQL sender. Same Connection panel on all of them. There is a Data Provider dropdown. There is a Connection String. There is the SQL underneath. There is not a separate timeout field sitting next to any of that.

The simple answer is that Sql Client already knows how long to wait. You tell it in the connection string. But that raises the next question: isn't a timeout just a timeout?

No. There are two of them, and they do different jobs.

Connect Timeout is how long to wait for the connection itself, in seconds. The instance is slow to accept you. The network path is having a moment. The server name is wrong and it sits there until it gives up. That clock starts before any SQL runs.

Command Timeout is how long a query or command may run once you are connected, also in seconds. A SELECT that scans more than it used to. A command waiting on a lock. A table that has grown since the last time anyone looked at it. Different clock.

Raise Connect Timeout when you cannot get in. Raise Command Timeout when you got in, then the query sat there. Read the error. Integration Host tells you which wait you hit.

To me it feels like an awkward leftover from treating the connection string as a secret you paste once and never read again. However, it is what it is...

But then the important question is how to deal with it?

Well, now that I do my integrations in Integration Host, things have become simpler (yeah, like everything else). Open the activity. Look at Connection. Stay on Sql Client, not Sql Client (Legacy). The encryption post covers that dropdown.

Then add onto the connection string you already have. Do not start again. Keep Server, Database, and the credentials. Stick the two keys on the end.

Server=SQL01;Database=Orders;User Id=iface;Password=****;Connect Timeout=60;Command Timeout=120;

I used 60 and 120 because they are easy to read, not because they are magic. Use what the site actually needs. The values are seconds. Connect Timeout of 60 is one minute to get in. Command Timeout of 120 is two minutes for the query or command to finish.

If you only change one of them, change the one that matches the wait you are actually seeing. A connection error after a short pause is Connect Timeout. A query that starts and then dies later is Command Timeout.

The same Connection UI is on Database Query, Database Receiver, and the SQL sender. Same string. Same two keys. You set it on the connection the activity is using.

Give the wait the site needs. If a query needs two minutes because the table is large, say two minutes. If the connection is just slow to accept you, say that on Connect Timeout and leave Command Timeout alone.

Anyway. As soon as the query ran, there was a timeout, and Integration Host showed it. There is no timeout box. Add Connect Timeout and Command Timeout to the connection string, in seconds, and give the wait you actually need.

Friday, 4 September 2026

SQL Client encryption in Integration Host

I pointed a Database Query at a SQL Server I use all the time. As soon as it tried to connect, it failed. Integration Host showed the error clearly. It was not a bad password. It was not the wrong instance name. Newer Sql Client wanted encryption on, and that server was not set up for it.

Firstly, what does that actually mean?

It means the client now assumes Encrypt=True unless you tell it otherwise. The connection string you have been pasting for years may no longer be enough on its own. You get a failed connection before any SQL runs.

The simple answer is that Sql Client changed its default. But that raises the next question: isn't encryption just something the server does?

No. Both sides have to agree. The server has to be ready to talk TLS. The client has to ask for it, or at least accept it. If the client insists and the server is not configured, the handshake fails. You never reach the query. You get a connection error.

I have seen this look like a network problem. It is not. The instance is there. The credentials can be fine. Integration Host is using the current Sql Client, and that one wants encryption on by default.

To me it feels like one of those defaults that is correct on paper and awkward on a hospital SQL box that has been running the same way since someone stood it up. However, it is what it is...

But then the important question is how to deal with it?

Well, now that I do my integrations in Integration Host, things have become simpler (yeah, like everything else). Open the activity. Look at Connection. Data Provider first.

The dropdown shows Sql Client and Sql Client (Legacy). Stay on Sql Client. Not Legacy.

Integration Host Connection panel with the Data Provider list open

That is the Connection panel with the Data Provider list open. The red arrows sit on Sql Client and Sql Client (Legacy). Query Database and SQL Query sit underneath. Database Receiver uses the same Connection UI. The SQL sender does too.

Legacy is the old provider. Stay on Sql Client. If the server is not set up for encryption, tell the current client what you want, in the connection string you already have.

Do not start a new string. Keep Server, Database, and the credentials. Add this on the end:

Encrypt=False;TrustServerCertificate=True;

Encrypt=False turns off the new default. The client will not require a TLS handshake the server cannot do.

TrustServerCertificate=True is the other half. If something still presents a certificate the machine does not trust, this says to accept it. I put both on when the server is not set up for this.

If the server is set up for encryption, you do not need Encrypt=False. Leave the default alone, or set Encrypt=True so it is obvious in the string.

Try it in a dummy workflow first. Not the live one. A throwaway Database Query against a table you do not care about. Same Data Provider. Same style of connection string. A catalog that does not matter. Change one thing. Run it. Then copy the line you trust into the real activity.

Anyway. Newer Sql Client wants encryption on. If the server is not set up for it, the connection fails. Stay on Sql Client, add Encrypt=False;TrustServerCertificate=True; to the string you already have, and try it somewhere that is not live first.

Wednesday, 26 August 2026

Merging HL7 fields without counting pipes

I've been working on a broken ADT. I had a known-good message and a slightly wrong one, and I needed a corrected copy by taking a few fields from one into the other.

The usual way is miserable. You count pipes, you copy a value, you miss a caret, you invent a new bug. I have done that more times than I want to admit.

Firstly, what are you actually trying to do?

You are not merging documents. You are deciding, field by field, which version of a patient fact should win. Birth place should be Wellington, not London. Patient class should stay inpatient. A pending location that only exists on one side should be created rather than left off. The clinical meaning comes first. The pipes are just how it is written down.

I already showed how to see those differences in the browser. Tools, Compare, in HL7 Soup Online. Semantic list on the left, text on the right. This is the next step. Once you can see PID-23, how do you fix it without counting?

Each difference row has two small buttons. << and >>.

<< takes the right value into the left. >> takes the left value into the right. The arrow points at the side that will change. I keep forgetting that for the first ten seconds, then it does what it says on the tin.

Keyboard works too. Select a difference, left arrow or right arrow. You can walk the list quite quickly.

In my case the top bar said PID-23 Birth Place. Left: London. Right: Wellington. I wanted the left message to be the corrected one, so I hit << and London became Wellington. The list updated immediately.

If a path only exists on one side, << is disabled. You cannot take a right-hand value that is not there. The tooltip is honest about it: cannot take the right value because this path is not present on the right. >> then becomes something more useful. Take the left value into the right and create the missing HL7 structure.

That is the standout bit. It will pad out the fields or the segment so the path can exist. No hand-building a PV1 just so field 16 has somewhere to sit. I tripped up on this years ago in raw editors, adding pipes until the field "looked" like it landed in the right place. It often didn't.

That is also the part that feels different from the Windows HL7 Soup editor. Compare I can do in a few tools. This field-by-field merge, including creating the missing structure, is the browser one I keep coming back to.

Revert Block sits under the text compare. Undo the block you just merged if you picked the wrong way. I used it. More than once.

Keep going and the field rows roll up. Once enough of PID matches, the individual PID-5.2 and PID-23 rows collapse into a single PID CHANGED. A bit surprising the first time. The list you were working down changes as you go. Keep picking.

I started with about twenty-five semantic differences and drove them down to none.

The empty state says: No semantic differences were found between these two HL7 messages.

A few things this is not.

It is two-way only. There is no base message, no three-way compare, no conflict marker. You are the arbiter. Last write wins per field. If you needed a proper ancestor merge you are in the wrong tool, and I would rather it said so than pretend.

There is no "save merged as a new message" button. The merged text is just the left and right editors. Copy the result out yourself. Treat them as a working copy.

You still paste the right-hand side. You cannot pick two messages from the loaded list inside the dialog. Same limitation as the compare post.

And please do not paste PHI into the online app. Use synthetic data, or anonymize first. Real patient messages stay in desktop HL7 Soup, or in HL7 Soup Web if you want the browser editor with the data staying on your own machine. A share link puts the message in the URL after the #, so it is not sent to the server, but that is not encryption. I would still only share a fake.

If you want to click around on a synthetic ADT^A04 from the built-in samples, this opens one in HL7 Soup Online:

Open the sample ADT^A04 in HL7 Soup Online

One ADT is about 1.9 KB of URL. Fine for a sample. Painful if you tried to stuff a real batch in there. The message is in the link, so treat the link like the message.

Anyway. Known-good on one side, broken on the other, cherry-pick the fields that matter, copy the winner out. No pipe counting. That is the whole trick.

Tuesday, 18 August 2026

Comparing HL7 messages in a browser

I had two ADT^A04 messages this week. Same EMR, same hospital, same patient. One registered Sam Brown without a fuss. The other got rejected. If you put them side by side in Notepad they looked the same until you got to the street.

Firstly, what does that actually mean?

The one that worked had this PID:

PID|1||74232^^^MRN||Brown^Sam||19780429|M|||19 High \T\ Main St^^Albany^NY^12207||555-555-2004

The one that failed had this:

PID|1||74232^^^MRN||Brown^Sam||19780429|M|||19 High & Main St^^Albany^NY^12207

The simple answer is that one of them escaped the ampersand in "High & Main", and the other one typed it in raw. But that raises the next question: isn't that just a different way of writing the same street?

No. In HL7, & is the subcomponent separator. You can see it declared right there in MSH-2 as part of ^~\&. It is not a letter in the address. It is punctuation that splits a field into smaller pieces.

So when the sending system wrote High & Main and left the & alone, the parser stopped treating Main as part of the street. Main, or the rest of that street, now sits in a different location than it does in the working message. The working one wrote \T\, which is the official escape for an ampersand. Same letters on the screen. Different place in the message.

To me it feels like an awkward leftover from the original delimiter design. I can picture a perfectly ordinary street sign, and some interface from the early '90s treating the & as structure. However, it is what it is...

But then the important question is how to deal with it?

Well, now that I do this in HL7 Soup, things have become simpler (yeah, like everything else). Open the message you already have. Then Tools, Compare.

The left pane is the current message. Paste the other one on the right. That is the usual job. I put the working ADT on the left and the rejected one on the right.



The list on the left is SEMANTIC DIFFERENCES. Each row has a path, a tag, and a short description.

The street is the interesting one. The working message kept High \T\ Main St as a single street value. The rejected one split it. PID-11.1.1 Street or Mailing Address is ONLY ON RIGHT, value 19 High. PID-11.1.2 Street Name is ONLY ON RIGHT, value Main St. Those paths do not exist on the left, because \T\ never created the subcomponents. Same street on the screen. Different place in the message. That is the row that explains the rejection.

Click PID-11.1.1. The top bar says Street or Mailing Address. Left: (not present). Right: 19 High. You stop saying "something in the PID" and start saying Main is sitting in a different location.



The other differences are real too. The working message has a home phone in PID-13, 555-555-2004. The failing one stops the PID after the address, so that field is only on one side. I would mention it on the vendor call. PID-12 is empty on the left and not present on the right. That is a real difference as well. Some systems treat empty and missing as the same thing. Some do not. MSH-10 is just the two control IDs, OK001 versus BAD001. None of that is why the second one failed. The ampersand is.

The right side is EDITABLE TEXT COMPARE. Side by side, both sides editable, the usual red and green. Useful when you want to stare at the raw PID line. I still start with the semantic list. That is the bit that understands HL7 paths instead of characters.

Why start there?

Because a text compare will happily tell you that & and \T\ are different characters, then leave you to work out what that does to every field after it. I have counted those pipes in Mirth exports more times than I want to admit. One extra delimiter and you are staring at the wrong slot. Click PID-11.1.1 and it tells you the street moved. You do not have to remember which number the street is, and you do not have to re-count after someone stuffed an extra caret into the name.

If you want to try the same two messages, here they are. Working first, then the one that failed.

MSH|^~\&|EMR|CITYHOSP|ADT|CITYHOSP|202608191000||ADT^A04^ADT_A01|OK001|P|2.5
EVN|A04|202608191000
PID|1||74232^^^MRN||Brown^Sam||19780429|M|||19 High \T\ Main St^^Albany^NY^12207||555-555-2004
PV1|1|O
MSH|^~\&|EMR|CITYHOSP|ADT|CITYHOSP|202608191000||ADT^A04^ADT_A01|BAD001|P|2.5
EVN|A04|202608191000
PID|1||74232^^^MRN||Brown^Sam||19780429|M|||19 High & Main St^^Albany^NY^12207
PV1|1|O

Anyway. Two messages, one works, one doesn't, and Compare points at the unescaped street ampersand. That is the whole trick.

Next time I will cover the little << and >> buttons on each difference. Those let you cherry-pick a field from one message into the other without counting a single pipe.

Monday, 10 August 2026

 

Comparing HL7 Integration Engines: 2026 Review

Updated July 2026

Last year I compared some of the main HL7 interface engines and ended up choosing HL7 Soup Integration Host. A lot has changed since then. Mirth has completed its move away from open source, iNTERFACEWARE has taken a rather unusual new direction, and HL7 Soup Integration Host has a new name: Integration Soup.

My own thinking has changed a little as well. I used to give code-led engines extra credit simply because anything could be coded. After spending more time inside some of our older Lua interfaces, I am less convinced that writing everything ourselves is an advantage. Modern graphical engines are far more capable than they used to be, and they can remove a lot of repeated logging, connection, security and error-handling code without limiting the business logic.

Integration EngineLicense & CostEase of UseStandards & ConnectivityScalability & Deployment2026 View
Integration Soup
Previously HL7 Soup Integration Host
Commercial subscription with workflow, site and enterprise options. Pricing is by quote. A 30-day trial is available.Very high. Visual workflows, drag-and-drop transformations and readable message views cover most ordinary work. C# remains available when code really is the clearest answer.HL7 v2, FHIR, JSON, XML, CSV and now DICOM. MLLP/TCP, HTTP/S, REST/SOAP, OAuth, files and broad database support. Extension libraries add cloud, SFTP, validation, document and encryption activities.On-premise or cloud deployment, with Windows and .NET Core builds. Azure and AWS extensions, Git-based workflow deployment and product, site and enterprise licensing options.Strengths: The easiest product here to understand quickly. Version 4.0 added useful depth without losing the simple workflow model. HL7 Soup Web is becoming an excellent browser companion, and the migration tooling is a real advantage.
Weaknesses: Smaller vendor and installed base than the enterprise incumbents. I would like to see more of the full Integration Soup experience move into the browser.
Infor CloverleafCommercial enterprise product with quote-based pricing.Medium. GUI tools and wizards are improving, but complex work can still require specialist Cloverleaf and TCL knowledge.HL7 v2, FHIR, CDA, X12 and broad legacy connectivity. Includes API gateway, FHIR and Da Vinci capabilities.Very high scalability and high availability. Available on-premise, in the cloud or as a hybrid deployment, including container options.Strengths: Proven at large healthcare organisations, broad standards support and strong operational reliability.
Weaknesses: Enterprise cost and complexity. It remains a product that benefits from experienced specialists rather than occasional interface developers.
iNTERFACEWARE Iguana / IguanaXCommercial. Published pricing starts at US$10,000 per year for two integrations, with larger packages by quote.Low to medium. IguanaX has a more visual component model, but Lua and code remain central to building and maintaining the interfaces.HL7 v2, FHIR, JSON, XML, REST, databases, files and other formats through components, libraries and custom Lua.Cross-platform deployment with cloud options. IguanaX development is active, with high availability and SSO identified as current areas of focus.Strengths: Fast, flexible and still extremely capable when you have strong developers and want complete control.
Weaknesses: My experience of the older codebase has exposed how much repeated plumbing the customer can end up owning. More importantly, the company now describes itself as being largely run by its founder. Whatever the theory behind that decision, it creates a support and business-continuity concern for a production interface engine.
Rhapsody CorepointCommercial enterprise pricing based on organisation size, deployment model and requirements.Very high. Its graphical, analyst-friendly approach remains one of the strongest in the market, and Rhapsody Axon now adds integrated AI assistance.HL7, FHIR, REST APIs and other healthcare formats, with reusable actions, testing, monitoring and alerting.Enterprise scale with high-availability options. Available as SaaS, on customer infrastructure or as an iPaaS deployment.Strengths: Excellent GUI, support and enterprise record. It was named Best in KLAS again in 2026 and is no longer fairly described as simply Windows-only.
Weaknesses: Premium pricing and a closed platform. It can be more product than a smaller integration team needs.
Mirth Connect by NextGen HealthcareMirth Connect 4.6 and later are commercial and proprietary. Version 4.5.2 remains available as the final open-source release but is no longer maintained by NextGen.Medium. The channel GUI is familiar to a very large community, but JavaScript or Java is still needed for a lot of real mapping and workflow logic.HL7 v2, X12, JSON, XML, REST, SOAP, DICOM and commercial FHIR capabilities.Cross-platform and cloud-agnostic, with commercial clustering, security extensions and central monitoring through Mirth Command Center.Strengths: Huge installed base, a great deal of existing knowledge and very flexible scripting. The commercial product continues to be developed and supported.
Weaknesses: The maintained free edition is gone. The dated development experience and amount of scripting are harder to excuse once commercial licensing is part of the decision.
Qvera Interface Engine (QIE)Commercial with flexible subscription licensing and pricing based on requirements.High. A browser-based visual channel editor handles the main workflow, with JavaScript available for more complex requirements. Qvera now also promotes an AI Companion for help, mapping and troubleshooting.HL7 v2, FHIR, CDA, X12, DICOM, ASTM, databases, JSON, XML and web services.Cloud-optimised and container-friendly, with AWS, Azure and Google Cloud deployment and orchestration options.Strengths: Modern web interface, strong standards coverage, credible cloud deployment and a good reputation for support. It remains one of the strongest alternatives in this list.
Weaknesses: Smaller market share than the major enterprise platforms, quote-based pricing and some scripting for complex work.

So... what do I choose in 2026?

The answer is still Integration Soup, although I think I have better reasons for choosing it this year.

In the past I tended to see a code-first engine as the more powerful option. If everything is code, then surely nothing is out of reach. The problem is that you also take responsibility for everything you code. Looking through our existing Iguana libraries, a surprising amount of the Lua is there to manage connections, logging, errors, security and cleanup. It is important work, but it is also repeated work, and every copy becomes another place for a mistake or an old security decision to survive.

Modern graphical engines do not have the same limitations that put people off them years ago. A good workflow designer can handle the normal integration work while still allowing code where it is genuinely useful. That leaves the developer concentrating on mappings, transformations and the rules that are actually different for that interface.

Integration Soup 4.0 has moved a long way in that direction. It now has broader database support, DICOM, Azure and AWS extensions, stronger source-control deployment and tools for converting existing channels. The rename from HL7 Soup Integration Host also makes sense: this is now much more than an HL7 utility with an engine attached.

I particularly like what is happening in the browser. HL7 Soup Web is already a very good way to inspect, understand and share a message, and Integration Soup is beginning to take over from there when that message needs to become a real workflow. I would definitely like to see more of the Integration Soup experience move into the browser, but the direction looks right.

Corepoint and Qvera remain very credible alternatives, and Cloverleaf is still difficult to argue with at true enterprise scale. For the sort of work I actually want to build and maintain, though, Integration Soup gives me the best balance: a proper interface engine, a modern GUI, code when I need it, and far less code that I have to own simply to keep an interface safe and running.

Tuesday, 4 August 2026

I’ve just found a very handy feature in HL7 Soup Online for sharing an HL7 message with someone else.

Right-click on any message in the list and select Copy message as link. This puts a hyperlink onto your clipboard which you can then paste into an email, support ticket or chat message.

Right-click on your message and select Copy message as Link

When the other person clicks the link, the exact message opens for them in HL7 Soup Online. They can see the readable description of the message, click through the fields, and find out what the values mean without having to install anything.

Take a look at this link to see it in action.

This is particularly useful when the person you are sending it to doesn’t work with HL7 every day. Pasting the raw message into an email normally gives them a block of text full of pipes and carets. Sending the link lets them see that it is a patient registration, which details are for the patient, what sort of visit it is, and where each value sits in the message.

I can see this being useful when asking a software vendor about a particular field, adding an example to a support ticket, or explaining a message to someone who understands the workflow but not necessarily HL7.

There is one important thing to remember. The message is included in the link, so you should treat that link in exactly the same way as you would treat the original message. Don’t copy a live message containing patient information and send it as it is.

HL7 Soup Online has an Anonymize menu at the top of the screen. If you are starting with a real message, anonymize it before creating the link. I would still have a quick look through the result afterwards to make sure that any local identifiers or unusual fields have also been dealt with. If possible, a synthetic test message is always the safest one to share.

That’s really all there is to it. Right-click the message, copy the link and send it. The recipient gets the actual message in a tool that helps them understand it, rather than having to work it out from the raw HL7 themselves.

Monday, 3 August 2026

Moving an Iguana Database Interface

Take a fairly normal Iguana interface that receives a message and writes some of it to a database.

The useful part is quite small. Read a few values from the message, convert them into the format the database expects, and insert or update the record.

But that isn’t all the Lua we ended up with.

We also need to open the database connection, make sure it is still available, handle the error if it isn’t, log enough information to work out what happened, and make sure everything is closed properly afterwards. If the interface needs to retry, that needs to be dealt with as well.

None of this is unusual code. In fact, that is really the point. It is code for exactly the same problems that every database interface has.

I have been looking at this while starting to move our Iguana channels to Integration Soup. When I create the database step there, the connection and the normal failure handling belong to the engine. I configure what I want to do and concentrate on the values I want to write.

The result is that most of the old Lua doesn’t need to be converted at all. It wasn’t business logic. It was just everything we had written around the business logic to make it safe to run.

I must admit, the large Lua libraries had always made the interfaces feel quite powerful. If we needed something, we could code it. That is still useful, but it also meant we took responsibility for a lot of things that would probably have been better handled by the integration engine.

It also makes later changes harder than they need to be. We have some old TCP interfaces where TLS now needs to be added. With our own implementations, each one has to be checked and updated. In Integration Soup it is a connection setting, so the same tested implementation is used throughout.

There will still be places where I use code. Some mappings and unusual rules are simply easier that way. But I would much rather use code for the bit that makes an interface different, and let the engine look after the bits that are the same every time.

That is probably the biggest improvement I have seen from the conversion so far. I’m not losing the useful code. I’m finding out how much of the other code I no longer need.